1. #include <stdio.h>  
  2. #include <fcntl.h>  
  3. int key1(){  
  4.     asm("mov r3, pc\n");  
  5. }  
  6. int key2(){  
  7.     asm(  
  8.     "push   {r6}\n"  
  9.     "add    r6, pc, $1\n"  
  10.     "bx r6\n"  
  11.     ".code   16\n"  
  12.     "mov    r3, pc\n"  
  13.     "add    r3, $0x4\n"  
  14.     "push   {r3}\n"  
  15.     "pop    {pc}\n"  
  16.     ".code  32\n"  
  17.     "pop    {r6}\n"  
  18.     );  
  19. }  
  20. int key3(){  
  21.     asm("mov r3, lr\n");  
  22. }  
  23. int main(){  
  24.     int key=0;  
  25.     printf("Daddy has very strong arm! : ");  
  26.     scanf("%d", &key);  
  27.     if( (key1()+key2()+key3()) == key ){  
  28.         printf("Congratz!\n");  
  29.         int fd = open("flag", O_RDONLY);  
  30.         char buf[100];  
  31.         int r = read(fd, buf, 100);  
  32.         write(0, buf, r);  
  33.     }  
  34.     else{  
  1.         printf("I have strong leg :P\n");  
  2.     }  
  3.     return 0;  
  4. }  

r0? r3? 
레지스터를 보아하니 ARM 이다.

문제를 보면 key1 + key2 + key3 == 입력값 이면 flag를 출력한다.
어셈을 보면서 하나하나 따라가보자


ARM에서 pc(program counter)의 값이r0에 남는다.


r0는 return값이 들어간다.


program counter에는 다음에 실행할 명령어의 주소가 들어있다.


즉 pc는 사용된 주소 +4byte를 가지므로 


key1에는 다음 명령어가 실행될 주소인 8ce4가 된다.



pc의 값을 r3에 넣고 를 더해주는 것을 확인할 수 있다.


그렇다면 8d04 + 4 + 4 == 8d0c가 key2의 값이 된다.



lr을 r3에 넣고 r3을 r0에 넣는것을 확인할 수 있다.


lr은 함수 호출시 리턴될 값을 의미한다.


main함수를 따라가보자.



key3은 8d80이 된다.


key1 + key2 + key3 = key이고 정수를 인자로 받으므로 key=108400



앙기모띠

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

pwnable.kr -cmd1  (0) 2016.09.25
pwnable.kr -lotto  (0) 2016.09.25
pwnable.kr -random  (0) 2016.09.25
pwnable.kr -passcode  (0) 2016.09.24
pwnable.kr -echo1  (0) 2016.09.23
  1. int main(){  
  2.     unsigned int random;  
  3.     random = rand();    // random value!  
  4.   
  5.     unsigned int key=0;  
  6.     scanf("%d", &key);  
  7.   
  8.     if( (key ^ random) == 0xdeadbeef ){  
  9.         printf("Good!\n");  
  10.         system("/bin/cat flag");  
  11.         return 0;  
  12.     }  
  13.   
  14.     printf("Wrong, maybe you should try 2^32 cases.\n");  
  15.     return 0;  
  16. }  


key값과 random 을 XOR 연산을 하였을때 0xdeadbeef 가 나오면 된다.


random 시드가 없으므로 값이 고정되어 있을 것이다.


random값은 rand 함수가 실행된 다음에 저장될 것이다.



필자는 scanf ( )함수가 시작되기 전의 주소에 break point를 걸고 실행시켰다.

random 값은 rbp -0x4에 있으며 그 값은 0x6b8b4567이다.


a^b=c

c^b=a


XOR의 특징을 이용하여 키를 구한다.



key는 3039230856이다.

scanf( )가 정수를 취하므로 굳이 바꿀 필요 없겟다 그대로 대입하자.



앙 기모띠

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

pwnable.kr -lotto  (0) 2016.09.25
pwnable.kr -leg  (0) 2016.09.25
pwnable.kr -passcode  (0) 2016.09.24
pwnable.kr -echo1  (0) 2016.09.23
pwnable.kr -bof  (0) 2016.07.29
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. void login(){  
  5.     int passcode1;  
  6.     int passcode2;  
  7.   
  8.     printf("enter passcode1 : ");  
  9.     scanf("%d", passcode1);  
  10.     fflush(stdin);  
  11.   
  12.     printf("enter passcode2 : ");  
  13.         scanf("%d", passcode2);  
  14.   
  15.     printf("checking...\n");  
  16.     if(passcode1==338150 && passcode2==13371337){  
  17.                 printf("Login OK!\n");  
  18.                 system("/bin/cat flag");  
  19.         }  
  20.         else{  
  21.                 printf("Login Failed!\n");  
  22.         exit(0);  
  23.         }  
  24. }  
  25.   
  26. void welcome(){  
  27.     char name[100];  
  28.     printf("enter you name : ");  
  29.     scanf("%100s", name);  
  30.     printf("Welcome %s!\n", name);  
  31. }  
  32.   
  33. int main(){  
  34.     printf("Toddler's Secure Login System 1.0 beta.\n");  
  35.   
  36.     welcome();  
  37.     login();  
  38.   
  39.     // something after login...  
  40.     printf("Now I can safely trust you that you have credential :)\n");  
  41.     return 0;     
  42. }  



코드를 보면 main( ) -> welcome( ) -> login( ) 순으로 흘러간다.


welcome( ) 함수를 살펴보면 name 배열에 100byte를 입력받고 그것을 인자로 출력한다. login( )함수에서는 2개의 입력받을 받아 출력하는데 


scanf( )함수에 &가 없다.

또한 fflush(stdin)을 통해 버퍼를 비운다.


우리가 여기서 알고 가야할 점은

scanf( ) 는 &가 없으면 받은 인자를 주소로 인식한다.


gdb를 통해 분석해보자.



다음은 welcome을 disassembly 한 모습이다.

scanf( ) 가 실행되고 입력을 받은 이후를 봐야 하므로 실행이 된 이후 임의의 지점에 break point를 걸어준다. 필자는 0x804864d에 걸어주었다.



96byte 뒤의 주소를 덮어 씌울 수 있다.


그렇다면 원하는 곳의 4byte를 입력할 수 있다.


fflush(stdin)을 우회하기 위해 fflush의 got를 overwrite 한다.


이후 system("/bin/cat flag") 로 점프하면 플래그가 나올 것 같다.



exit( )함수를 사용할 것이므로 info function 명령어를 통해 exit( ) 함수의 주소를 알 수 있다.  exit( ) 함수의 주소는 0x08048480이다.



exit( ) 함수의 got 는 0x804a018이다.


/bin/cat flag 의 주소는 0x080485e3 이다.


페이로드를 작성하면

buffer(96) + exit@got + 0x080485e3의 10진수 + "\n" + "asdfasdf" + "\n"

이정도가 될것이다


10진수를 취하는 이유는 scanf가 %d , 즉 정수를 인자로 받기 때문이다.



앙 기모띠

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

pwnable.kr -leg  (0) 2016.09.25
pwnable.kr -random  (0) 2016.09.25
pwnable.kr -echo1  (0) 2016.09.23
pwnable.kr -bof  (0) 2016.07.29
pwnable.kr -flag  (0) 2016.07.15


ROP 를 공부하는 사람들이 가장 많이 풀어보는 ropasaurusrex 문제이다.


먼저 ROP가 무엇인지 알아보자.


ROP (Return Oriented Programming)


프로그램 내부에 있는 기계어 코드 섹션(Gadget) 을 이용한 Buffer Overflow 공격 시 특정 명령을 실행시키는 방법을 말한다. 


보통 가젯은 함수끝에 있는 ret 명령어를 포함한 상위 몇가지 명령어들의 집합이며 이를 이용하여 단번에 공격을 성공할 수 있다.


ROP 는  RTL, RTL Chaining, GOT overwrite 3가지의 기술을 사용한다.


(1) RTL

(/bin/sh)을 실행하기 위해 system( ), exec*( )등과 같은 함수를 사용하여 공격


(2) RTL Chaining 

RTL 공격을 연속적으로 일어나게 스택을 구성하여 공격하는 방법.

연속적으로 함수를 호출할 수 있다.


(3) GOT Overwrite

실제 함수 주소를 저장해 놓은 공간인 GOT에 특정한 함수의 GOT로 변경하여 원하는 함수가 실행되게 하는 방법. (PLT.GOT 정리 참고)



ROP에서 가장 중요한 것은 라이브러리 이다. 라이브러리는 바이너리 내에 있는 주소 이므로 가젯으로 사용이 가능하다. 하지만 라이브러리의 주소는 0x00653a0 와 같은 형태로 되어 있기 때문에 그대로 사용해서는 안된다.  


그 이유는 바이너리 내의 라이브러리는 imagebase 라는것이 존재하기 때문이다. 


이것은 라이브러리의 시작주소를 의미한다.


예를들어 imagebase가 0xf755b00이고  system 주소가 0x1234567 이라고 가정하면 0xf755b00 + 0x1234567이  system주소가 된다.


하지만 라이브러리는 보통 ASLR 때문에 이미지베이스가 계속 바뀐다.


imagebase를 구하는 방법은 예를들어


write@libc = 0x12345 

실제 write@libc = imagebase + 0x12345

imagebase = write@got - write@libc (0x12345)


다음과 같이 구할 수 있다^^


문제를 풀기에 앞서 일단 file 명령어를 이용하여 파일의 속성을 살펴보자.



32bit ELF가 strip 되어 있다,


strip은 오브젝트 파일에 있는 심볼을 삭제하는 툴이다.


일반적으로 빌드가 완료된 실행파일 또는 라이브러리에서 불필요한 심볼을 제거하는데 사용되고 있다. 사용하게 되면 디버깅을 할때 함수명이 보이지 않는다.


보호기법이 걸려있는지 확인하기 위해 pwntool 라이브러리를 이용하였다.


설치방법은 pip install pwntools 명령을 이용해 설치할 수 있다.


선행으로 pythonp-pip와 python-dev가 설치되어있어야 하며


이것만 설치할 경우 ROP클래스를 호출할때 disasm 오류가 발생한다.


따라서 apt-get install libcapstone-dev명령을 이용하여

libcapstone-dev를 설치해준다.


python을 이용하여 다음과 같이 입력한다


>>>from pwn import*

>>>elf = ELF("./ropasaurusrex")



NX(No Excutable) 가 활성화 되어있음을 확인하였다.


NX란 메모리 보호기법 중 하나이다.

메모리 페이지의 write권한과 execute 권한을 동시에 갖지 않도록 설정한다.

메모리에 execute 권한이 없으므로 쉘코드를 실행시킬 수 없다.


ROP를 이용하여 NX 를 우회할 수 있으므로 이를 이용하여 문제를 해결한다.


해당 바이너리를 IDA 로 열어보면 다음과 같은 소스코드를 볼 수 있다.





0x88 == 136byte만큼 할당을 받았지만.

0x100 == 256byte를 입력받는다.


명백한 오버플로우 취약점이다.


이제 ROP공격을 위해 함수들의 주소를 구해야 한다.


1. read@plt, read@got

2. write@plt, write@got

3. bss

4. pop pop pop ret Gadget

4. systemoffset

5. readoffset


일일히 하면 시간이 걸리므로 pwntool 라이브러리를 이용한다.


payload를 구성 위해서 우리가 구해야 할 것은 


pppr과 readoffset systemoffset 세가지이다. 


objdump -d ropasaurusrex



pppr : 0x80484b6



objdump -d /etc/lib32/libc.so.6 | grep "system"



system offset : 0x3fe70



objdump -d /etc/lib32/libc.so.6 | grep "__read"



read offset : 0xd9880



일일히 하면 시간이 걸리므로 pwntool 라이브러리를 이용하여 

다음과 같이 Exploit Code를 만든다.




'pwnable' 카테고리의 다른 글

시스템 해킹이란?  (0) 2017.02.07


PLT (Procedure Linkage Table)


실제 호출 코드를 담고 있는 테이블

 내용 참조를 통해 실제 시스템 라이브러리 호출이 이루어진다.


GOT (Global Offset Table)


GOT는 PLT 가 참조하는 테이블로써 프로시져들의 주소를 가지고 있다. 

PLT가 어떤 함수를 호출할 때 이 GOT를 참조해서 해당 주소로 점프하게 된다.



간단한 예제를 통해 PLT 와 GOT를 이해해보자.


사용할 소스코드는 다음과 같다



비교적 간단한 소스코드이다.


사용하는 운영체제가 64bit 이므로

32bit로 깔끔하게 컴파일 해주기 위해 -fno-stack-protector -m32 옵션을 주었다.



이 옵션을 주기 위해서는 gcc-multilib가 필요하다.

apt-get install gcc-multilib 명령어를 이용해 설치할 수 있다.


이제 gdb를 이용하여 만들어진 바이너리를 열어보자.



disas main을 해보면 0x80482c0(puts@plt영역에 call하는 것을 볼 수있다.


printf 함수가 내부적으로 puts을 호출하나 보다 -ㅅ-


살펴보자.



PLT 영역을 보면 puts에 대해서 3단계로 구성되어 있는것이 보인다 


그럼 첫 번째 jmp는 당연히 GOT영역이 된다는 것을 알 수 있다.



GOT 부분을 보면 PLT의 두번째 부분을 가리키는 것을 알 수 있다.


이는 첫번째 호출이기 때문이다.



문 득 공부하다보니!


첫번째 호출이 아니라면? 이라는 의문을 가질 수 있었다.


printf 함수를 한번 사용해본 상태에서 break point를 걸고 확인해보자.



printf 가 한번 실행됬다.



0x80496c4 의 값이 바뀐 것을 알 수 있다.



'pwnable > 기타' 카테고리의 다른 글

songsari's prob4  (0) 2016.09.12
songsari's prob3 (RTL)  (0) 2016.09.10
songsari's prob2 (Command Injection)  (0) 2016.09.10
songsari's prob1 (MEMORY_LEAK)  (0) 2016.09.10
RTL 심화  (0) 2016.08.09

+ Recent posts