해킹/개념 정리 - 10

  1. '|'(파이프)를 사용한 페이로드 작성 2016.01.21
  2. 간단한 ASLR 우회/차단 기법 2016.01.16
  3. 25바이트 쉘코드를 작성해봤다. 2016.01.16
  4. 쉘코드 작성법 2016.01.16
  5. ASLR의 개념 2016.01.13
  6. 함수의 프롤로그와 에필로그에 관한 고찰 + fake ebp(실패) 2016.01.13
  7. 다중 파이프 페이로드 2015.11.24
  8. GOT overwrite 2015.11.24

'|'(파이프)를 사용한 페이로드 작성

해킹/개념 정리 2016. 1. 21. 14:28

시스템 관련 문제들을 풀다보면 stdin으로 특정 16진수를 넣어줘야 하는 상황이 꼭 발생한다.

이럴경우 해당 16진수가 ascii상 키보드로 입력할 수 없는 값이면 어쩔수 없이 프로그램으로 전달해줘야 한다.

그리고 이럴때 간단하게 사용할 수 있는 perl이나 python을 주로 이용하게 된다.


물론 c언어 등 다른 언어에서도 pipe 함수를 제공하여 프로세스간의 통신을 제공한다.

예컨데 c언어에서 특정 프로그램에 stdin으로 값을 집어넣으려면 파이프로 사용할 배열을 만들고 pipe()함수를 사용해서 커널에 파이프를 만들 수 있다. 그리고나서 fork()함수를 사용해서 부모-자식간에 공유되는 pipe변수를 가지고 통신을 하는 방식이다.


http://satanel001.tistory.com/33

이 포스팅에 조금더 자세한 구현방식이 나와있다.



하지만 위 방법은 코드도 길고 컴파일도 해야해서 bash에서 바로 사용할 수 있는 파이썬 스크립트와 '|' 사용에 비하면 효율성이 많이 떨어진다.

'|'(shift+\)는 bash에 들어갈 경우 특이한 기능을 하는데 '|'를 기준으로 좌측 부분의 실행결과 stdout으로 출력되는 부분을 우측 프로그램의 stdin으로 넣어주는 역할을 한다. 좀더 자세한 설명을 위해 테스트 프로그램을 작성해봤다.





       

   

받은 입력을 그대로 출력하는 get_print와 hello world를 출력하는 helloworld라는 프로그램을 각각 작성해주었다.

그리고 ./helloworld | ./get_print 라고 명령어를 입력해주면





이렇게 helloworld의 출력이 get_print의 입력으로 들어가서 input은 hello world라고 말해주는 것을 볼 수 있다.

여기서 helloworld를 c를 이용한 프로그램이 아닌 bash에서 바로 사용할 수 있는 간단한 python 스크립트로 바꿔주면


(python -c 'print "hello python!!"')|./get_print

이렇게 쓸수 있다. 결과를 보면




역시 입력값이 제대로 들어가는 것을 확인했다.

이런식으로 파이프를 기준으로 좌측에 python 스크립트를, 우측에 입력을 넣고자 하는 프로그램을 놓아서 입력을 전달할 수 있다.



하지만 만약 쉘코드를 사용해서 페이로드를 작성한다면 (python -c 'print "~~"';cat)|./input 이런식으로  ;cat을 붙여줘야 한다.

이유는 파이썬 스크립트가 출력하는 과정에서 마지막에 EOF를 넣기 때문이다. 스크립트가 정상적으로 쉘을 띄우더라도 그 쉘이 EOF를 만나서 꺼져버린다고 한다.





LOB의 전형적인 파이프를 이용한 bof 문제인데 보이는것처럼 같은 입력이더라도 ;cat이 없을때는 쉘이 실행되지 않는 것을 볼수있다.




Tags
Social

간단한 ASLR 우회/차단 기법

해킹/개념 정리 2016. 1. 16. 22:26

스택의 주소가 필요한 공격을 할때 상당히 짜증나는 보호기법중 하나가 aslr이다. 

ASLR의 개념: http://satanel001.tistory.com/60

위 글에서 설명했듯이 Imagebase를 랜덤하게 해줘서 주소 예측이 힘들게 하는 방식이다.


이 aslr에 대해서 사소하더라도 내가 알고있는 우회/차단 기법을 몇가지 소개한다.


1.0x90사용하기

쉘코드를 사용하기 위해서 많이 쓰는 방법이다. 0x90(nop)이라는 명령어는 프로그램에 아무런 영향도 끼치지 않고 그냥 eip를 다음 명령어로 넘겨버린다. 그러므로 이 0x90을 많이 늘어놓은 다음 뒷쪽에 쉘코드를 넣어서 프로그램이 랜덤한 주소로 리턴하거나 점프해도 0x90안에 eip가 떨어질 확률을 높히는 것이다.

여러개의 0x90중 한군데에만 떨어져도 eip는 0x90을 타고 끝까지 가서 쉘코드를 실행하게 된다. 많이 쓰이는 방법이긴 한데 버퍼의 크기가 한정되어있으면 0x90을 넣을 공간이 부족할 수 있다.


2.반복실행

0x90과 마찬가지로 확률을 높히는 방식이다. 원하는 주소가 어딘지는 모르지만 가능한 주소를 골라서 될때까지 계속 시도를 하다보면 랜덤의 특성상 언젠가는 해당주소에 걸릴 것이다. 0x90과 함게 사용하면 확률을 더 높힐 수 있겠다. 하지만 너무 반복적으로 실행하지 못하게 차단한다던지 하는 환경에서는 사용할 수 없는 방법이다.


3.ulimit -s unlimited

리눅스 소스의 mmap.c 부분에 32비트에 stack 크기가 unlimited일경우 이미지베이스에 랜덤값을 추가하지 않도록 되어있다.

그냥 소스 자체가 그렇게 쓰여있는 것이다.

아마도 Imagebase에 작은 숫자라도 랜덤한 값이 추가되면 가상메모리의 최대범위를 넘어갈 수 있기 때문이 아닐까 조심스레 추측만 해본다.


그런데 이게 내가 알고있던 것 처럼 항상 스텍영역의 주소를 고정해주는 것은 아닌듯 하다. 우선 기본적으로 64비트에서는 안되고...

간단한 코드로 실험을 해봤는데. 우분투 32비트에서도 실패했다.




이런식으로 스텍에 할당된 버퍼와 환경변수의 주소를 각각 출력하도록 test.c를 짜줬다.

그리고 이 프로그램을 그냥 5번 실행하는 runtest라는 프로그램을 작성해서 돌려봤더니..




캡쳐와 같이 ulimit -s가 unlimited인데도 다 다르게 나온다.

부모 프로세스의 ulimit에 따라 다르다고 해서 /etc/security/limits.conf에 기본 ulimit -s도 다 unlimited로 바꿔줬는데도 여전히 주소가 바뀐다.

하지만 분명히 사람들이 이 방식으로 aslr을 우회할 수 있다고 하고 나도 실제로 사용해본 경험이 있기에 일단 소개는 해놓았다..



4.setarch (아키텍쳐) -R

내경우에는 setarch linux32 -R다.



setarch의 옵션들인데 -R은 addr-no-randomize 옵션이라고 쓰여있다.

이 명령어를 실행하면 쉘이 하나 뜨는데 그 안에서 실행하는 프로그램의 aslr이 풀린다.




위 캡쳐처럼 주소가 통일되었지만... suid나 sgid가 걸린 파일에는 아래처럼 다시 랜덤해져버린다...

실제 suid가 걸린 파일을 익스플로잇 해야할때는 사용하기 힘들다는 뜻..



결론은 대부분의 경우 가장 기초적인 1, 2번 방법에 기대야 하고 3.ulimit -s unlimited는 분명이 써먹을 수 있었던거 같은데 공부가 좀더 되고 확인이 되면 다시 글을 쓰는걸로 해야겠다...




2016-01-20 추가: ulimit -s unlimited는 힙과 라이브러리 영역까지만 고정이 되고 스택영역은 고정이 안된다고 한다.

Tags
Social

25바이트 쉘코드를 작성해봤다.

해킹/개념 정리 2016. 1. 16. 12:27

쉘코드 작성법


위 글에서 쉘코드 작성법에 대해서 다뤘었는데 개념은 어느정도 가지고 갔지만 평소 내가 쓰던 25바이트 쉘코드에 비해 2바이트가 더 긴 27바이트 쉘코드가 완성됐었다.

공간효율은 높을수록 좋으므로 2바이트 더 작은 쉘코드를 작성하는 방법에 대해서도 공부해서 올려본다. 

작성법에 대한 자세한 설명은 위 포스팅에서 했으므로 그냥 더 효율적인 방법을 소개한다는 생각으로 코드만 간단히 해설한다.




발상이 좀 특이한데 /bin/sh 은 총 7바이트로 이것을 스텍에 push하고 사용하려 하면 0x00이 중간에 들어갈 수밖에 없다.

그런데 이게 /bin//sh 이런식으로도 실행이 되는 모양이다.




실제로 해보니 정말로 /가 아무리 중첩해서 들어가도 프로그램을 실행하는데에는 지장이 없다.

자 그럼 /bin//sh 이렇게 8바이트를 만들어줘서 0x00가 들어가지 않도록 해줄 수 있다.




그래서 이런 코드를 짤 수 있다.

지난번과 다르게 문자열을 가장 밑에 둘 필요가 없으니 점프하고 콜하는 불필요한 부분이 사라졌다.


코드를 간단하게만 설명하면 먼저 eax를 xor해서 0으로 만든 뒤 스텍에 push한다. - 문자열의 끝을 알리기 위한 널이다.

그리고 차례로 //sh, /bin을 스텍에 push해준다.

그리고 이 문자열의 주소를 가르키고 있는 esp를 %ebx에 넣어준 후

다시 eax와 ebx를 push 해준다. eax는 배열의 끝을 알리는 널, ebx는 아까 문자열을 가르키는 포인터다.

그리고 이 ebx가 있는 스텍의 부분을 가르키는 esp를 ecx에 넣어주면 중요한 부분은 끝이다.

나머지는 특별할것 없는 인자전달 부분이다.


스텍 그림을 그려보면

||$ebx||0x00||/bin//sh||0x00||

^                          ^

ecx                     ebx


이런식으로 ecx에 더블포인터가 들어가는 문제까지 해결됐다.


최종적인 바이너리 쉘코드는 위의 코드를 쭉 이으면 된다.

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\x31\xd2\xcd\x80


Tags
Social

쉘코드 작성법

해킹/개념 정리 2016. 1. 16. 03:49

해야지 해야지 하고 미뤄뒀던 쉘코드 작성법이다.

사실 나도 내가 짠 쉘코드를 사용한다기 보다는 남들이 해놓은거 바이트별로 모아놨다가 써서..

쉘코드 작성법에 대해서는 별로 숙달도 되어있지 않고 무엇보다도 기계적으로 학습한 부분도 있어서 설명이 부족할 수 있다.



우선 쉘코드라는 것은 말그대로 쉘을 실행시켜 주는 코드다... 이렇게 말하면 좀 막연하니 부연하자면

대부분 프로그램을 짤때는 c, java같은 하이레벨 랭귀지를 사용한다... 길어서 쓰기 귀찮은데 한국말이 안떠오른다... 고급 언어라고 부르겠다.

쉘코드는 주로 공격용으로 만들어지는데 공격할 프로그램 어딘가에 이런 고급언어로... 예를들어 system("/bin/sh"); 이런식의 문자열을 넣어놓고 eip를 이쪽으로 점프시킨다고 해도 당연히 컴퓨터는 알아먹지 못한다.


그래서 쉘을 실행시키는 기능을 컴퓨터가 이해가능한 기계어 수준 즉, 바이너리로 작성하는 것이다.

거기에 argv나 gets 함수등 문자열의 형태로 보통 집어넣기 때문에 \x00이 있으면 문자열의 끝으로 인식하여 들어가지 않는 문제를 해결하기 위해서 \x00을 빼주는 작업을 한 것이 바로 쉘코드다.


굳이 쉘코드를 집어서 글을 쓰는 이유는 쉘을 띄우면 해당 권한의 어떤 명령어든 써먹을 수 있어서 가장 많이 쓰이기 때문이다.

ftz나 lob같은 문제에서는 execve("my-pass");이런것을 코드로 만들어 써도 괜찮을듯 하다.


서론이 길어졌는데.. 그래서 기계어를 보려면 당연히 프로그램을 짜서 그 바이너리를 보는게 빠르다.



이렇게 생겨먹은 shell.c라는 코드를 짜서

gcc -o shell shell.c -static 이렇게 컴파일 했다.

-static이 들어간 이유는 공유라이브러리가 아닌 정적 라이브러리를 사용하기 위함이다.

쉽게말하면 execve가 실행되기 전에 execve의 코드를 보기 위해서 필요하다.






그리고 gdb에서 shell 프로그램을 열어서 execve를 disass 해주면 -static 옵션으로 컴파일 했기 때문에 execve 코드가 이렇게 쭉 나온다.

저기에서 mov 하고 ebp+어쩌구라고 되어있는 부분이 인자를 전달하는 부분인데 ebp+8부분이 /bin/sh라는 문자열의 주소, ebp+0xc 부분과 ebp+0x16부분이 아까 c에서 짤때 널로 넣어준 부분이다.

직접 확인해 봐도 좋지만 함수호출시 인자 전달 순서정도는 외워두두는 것도 편하다.

이렇게 인자를 전달하고 나서 eax에 0xb를 집어넣고 int 0x80을 하는데 우리가 필요한건 딱 이 int 0x80까지의 부분이다.



int 0x80에서 끊고 뒷부분은 필요 없는 이유는 저부분에서 실질적으로 필요한 동작이 끝나기 때문인데..

int 0x80명령어가 실행되면 프로세스는 커널에 인터럽트를 발생시키며 eax, 정확히는 al에 들어있는 0xb라는 저 숫자를 커널에 전달한다.

0xb=11, 이 11이 무슨 숫자냐하면 시스템 콜 넘버다.

http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Assembly/Documents/article_linux_systemcall_quick_reference

여기서 리눅스 시스템 콜 레퍼런스를 보면 11번에 execve가 있는것을 확인할 수 있다.

그래서 커널이 execve를 실행하게 되면 프로그램은 완전히 /bin/sh라는 프로그램으로 덮어지므로 뒷쪽 코드는 필요없다.

저부분은 아마도 에러나 실수 등으로 execve가 실패했을 때의 처리 부분일 것이다. (아마도다. 아마도...)


그럼 이제 shell_asm.s라는 파일을 만들어서 해당부분만 어셈블리어로 작성해보자.

위 분석에서 확인한 바로는 


eax로 시스템 콜 넘버 0xb

ebx로 "/bin/sh"의 주소

ecx로 0x00(null)을 가르키는 주소 (const char* 형태라서 주소를 넣어줘야 한다.)

edx로 0x00(null)을 넣어주면 된다.






이런식으로 넣어줬다. 어셈 프로그래밍은 거의 안해봤지만 그럼에도 어려운 수준은 아니다.

strings에서 exeshell을 call 하면 call 명령어에 의해 스텍에 리턴주소가 들어가게 된다.

그 리턴주소는 strings에서 바로 다음 부분이므로 "/bin/sh"의 주소가 된다. 이 리턴주소를 popl ebx 해서 ebx에 집어넣어줬다.

그리고 pop 0x0을 해서 스텍에 널을 집어넣고 그 널을 가르키고 있는 esp를 ecx에 넣어줘서 널을 가르키는 주소를 완성했다.

eax와 edx는 그냥 movl 명령어로 넣어준 것이니 굳이 설명은 필요 없을 것 같다.


굳이 strings를 아래쪽에 둔 이유는 아마도 문자열인 "/bin/sh"를 맨 밑으로 내려놓기 위함이다.

그래야 0x00이 중간에 들어가지 않고 쭉 연결되게 만들수 있기 때문일듯. 나도 약간은 수동적으로 외운 부분이라 확실하지는 않고 추측일 뿐이다.


컴파일 하기 전에도 지금 edx에 집어넣을 0x00이 코드 중간에 박혀있는 문제를 예상할 수 있지만 그게 다가 아니니 일단은 컴파일해서 바이너리를 살펴보자.

objdump의 -d 옵션을 사용하면 바이너리와 코드를 함께 볼 수 있다.






......윗부분은 무시하고...

보아하니 00이 꽤 많이 들어가있다.

edx에 0x0을 넣어줄때 00이 들어갈 것은 예상했지만 movl을 사용해서인지 eax에 0xb를 넣을때도 00이 세바이트나 들어갔다.

이것들을 없애주는 작업을 할건데 방법은 간단하다.

edx는 xor edx edx 이런식으로 0으로 만들어준다.

eax는 똑같이 xor eax eax 해서 4바이트를 00000000으로 만들어준뒤,

eax의 끝바이트를 뜻하는 al 레지스터에다가 movb로 0xb를 넣어준다.

이 방법대로 코드를 다시 짰다.





push 0x0 에서 0x0대신 xor로 0으로 만들어준 eax를 쓰느라 순서가 약간 뒤집혔지만 자세히 들여다보면 위와 같은 코드다.

이제 이것을 objdump로 분석해보면






이렇게 널이 없는 기계 코드를 얻어냈다.

밑에 0x00부터는 상관없는 부분이므로 떼주면


"\xeb\x0c\x31\xc0\x5b\x50\xb0\x0b\x89\xe1\x31\xd2\xcd\x80\xe8\xef\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"



이제 테스트를 해봐야 하는데 테스트 프로그램 만들기가 귀찮아서 그냥 ftz level11을 켜서 bof 테스트를 해봤다 ----> 실패했다...

코어덤프들을 보며 분석해본 결과 문제는 ecx로 넘어간 주소가 널을 가르키고 있기 때문이라고 생각했다.

원래 argv[0]에는 프로그램명이 들어가야 하므로 "/bin/sh"의 주소가 들어가는게 맞긴 하다.

그런데 분명히 c언어에서 NULL을 넣고 돌렸을땐 돌아갔는데... 참 이러면 억울하다. 환경에 따라 다르거나 컴파일러가 어떤 예외처리를 해줬거나 둘중 하나인듯.





결론은 이렇게 해결해 줬다. 추가된 부분은 push ebx 부분인데 어떻게 해결이 된거냐하면

||argv[0]의 시작주소 ||argv[1]의 시작주소||

||ebx(/bin/sh의 주소)||\x00000000        ||

^

esp

이렇게 ebx를 스텍에 넣어준뒤 esp를 통해 해당부분의 주소를 전달하여 성공한 것.... 뒤에 널까지 굳이 또 넣어준 이유는 이렇게 안해주면 배열로 인식을 안하는 모양이다... 참 까다롭기 그지없다.

그래서 위 objdump에서 뽑아낸 쉘코드는


"\xeb\x0d\x31\xc0\x5b\x50\x53\xb0\x0b\x89\xe1\x31\xd2\xcd\x80\xe8\xee\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

27바이트짜리 쉘코드다.




중간 풀이과정은 생략하도록 하고... 위 쉘코드를 사용해서 bof 공격에 성공했으므로 코드가 작동한다고 봐도 무방한 것 같다!!

해야지 해야지 하다가 결국 쉘코드 작성도 해보고 포스팅까지 마쳤다.... 이제 다양한 형태의 쉘코드를 요구하는 문제에도 대응할 수 있을듯

끝-




ps.다른 쉘코드를 gdb에 넣고 분석해 보니 /bin/sh를 스텍에 밀어넣고 사용하는듯 하다. 그러면 처음에 main에서 아래로 점프할 필요가 사라져서인지 2바이트 적은 25바이트로 쉘코드를 만들수 있었다. 더 효율적인 방법이 있으므로 실전에서는 저방식대로 만드는게 좋을듯...




ps2.http://satanel001.tistory.com/76 에 25바이트 쉘코드에 대한 정리를 추가했다.

Tags
Social

ASLR의 개념

해킹/개념 정리 2016. 1. 13. 15:20

ASLR-Adress Space Layout Randomization


내가 이해한 ASLR은 프로세스가 메모리에 올라갈 때 힙, 스텍, 공유 라이브러리의 위치를 랜덤하게 정해주는 것이다.

고정주소를 알아야 가능한 일부 익스플로잇 기법에 대해서 효과적인 대처가 될 수 있으나, 또 무식하게 주소가 맞을때까지 여러번 돌리면 공격이 성공할 수 있다는 단점 또한 가지고 있는 듯 하다.


아무튼 이렇게 프로세스 내에서 힙, 스텍등의 위치를 랜덤하게 하기 위해서 Image Base값을 랜덤하게 바꿔준다고 한다.

Image base에 관해서도 설명을 해야할 것 같은데..

이름에서 어느정도 느껴지듯이 가상 시작점이라고 생각하면 이해가 좀 편할듯 하다.

프로세스의 가상 메모리의 절대주소 VA값은 이 Image base값 + RVA(상대주소)로 정해진다.

쉽게말해서 어떤 스텍이 0xdeadbeef라는 주소에 위치한다면 Image base값 0xdead0000에 RVA 0x0000beef값을 더해서 0xdeadbeef라는 주소에 위치하게 된다는 것이다. 그런데 여기서 Image base값을 랜덤하게 정해줘 버려서 해당 스텍의 주소가 0xdeadbeef가 될지 0xcafebeeef가 될지 뭐가될지 공격자가 알수 없게 하겠다는 발상이다.


정확히 Image base와 RVA가 2바이트씩 떨어지는지는 확인한 바가 없다... 그냥 개념이 저렇다는 것이다.


ASLR을 우회할 수 있는 방법이나 좀더 깊히 들어가야 하는 내용은 필요할 때 그때그때 다른 포스팅에서 다루고 이 포스팅에 링크를 걸어놓는 방식으로 해야겠다.

Tags
Social

함수의 프롤로그와 에필로그에 관한 고찰 + fake ebp(실패)

해킹/개념 정리 2016. 1. 13. 00:17




이 실험의 기본적인 목적은 함수의 플롤로그와 에필로그에서 어떠한 일이 일어나는지 관찰하는것.

그것을 용이하게 하기 위해 굳이 간단한 출력을 func()라는 함수까지 만들어서 하고 있는 것이다.

그리고 그 에필로그와 프롤로그를 잘 관찰한 뒤에 facke ebp를 사용해서 SFP까지만 덮어서도 프로그램의 흐름을 바꿀 수 있는지 시도해보려고 한다.



먼저 그림과 같이 func를 call 하는 라인에 브레이크포인트를 걸었다. 해당 부분까지 실행해보면...



이부분에서는 레지스터만 보고 넘어간다.

esp:0xbffff780

ebp:0xbffff788

을 각각 가르키고 있다. stepi를 해서 func 안으로 들어가서 다시 레지스터를 확인해 보자





esp:0xbffff77c

ebp:0xbffff788

ebp는 그대로인데 esp가 -4만큼 늘어났다. esp에 뭔가 push 되어있다는 뜻이다. 해당 부분을 확인해봤다.





esp가 가르키고있는 0xbffff77c 메모리 주소에는 0x0804854a가 들어있었다. 이 주소는 main+11,

call func를 하는 main+6의 바로 다음주소, 즉 리턴주소가 들어간 것이다.

call 명령어가 실행되면 단순히 그자리로 점프하는 것이 아니라 현재 esp가 가르키고 있는 자리에 돌아올 주소를 push하고 점프하는 것이다.


이제 func에서 실행될 명령어는 함수의 프롤로그인 push ebp; mov ebp, esp 명령어들이다.

사실 자명하긴 하지만 그래도 과정을 확실히 이해하기 위해 하나씩 돌려봤다.




esp:0xbffff778

ebp:0xbffff788

보다시피 esp가 추가로 4만큼 빠지면서 그 자리에 0xbffff788이 들어갔다. ebp에 들어있던 값이다.

이부분이 바로 다시 main함수로 return 할때 ebp에 저장될 SFP라는 값이다. leave라는 과정에서 SFP가 ebp롤 pop 되어서 올라간다.

이제 mov ebp, esp 부분이 실행될텐데 당연히 ebp가 현재 esp의 위치까지 당겨질 것이다.

마찬가지로 과정을 이해하기 위해서 인내심을 가지고 돌려보자.




esp:0xbffff778

ebp:0xbffff778

예상과 같이 ebp를 당겨왔다. 파란글씨로 esp와 ebp를 쓰는 이유가 나중에 두 레지스터를 기준으로 그림을 그려보기 위해서다.

이제 프롤로그를 다 봤으니 에필로그인 leave ret 부분까지 넘어가보자.





leave앞에서의 레지스터 상황이다. 

함수의 에필로그는 leave || ret 두가지로 이루어진다.

leave는 mov esp, ebp; pop ebp 두가지를 하나의 명령어로 묶은 것이고.

ret은 pop eip; jmp eip 두가지 명령어를 하나로 묶은 것이다.

내가 이해하기로는 프롤로그인 call ~~~; push ebp; mov ebp, esp 의 완벽한 역과정인것 같다. 확실히 대칭을 이룬다.

이제 leave를 하면 ebp 뒤에 있는 0xbffff788이 ebp로 들어갈 것이다. 




예상대로 스텍에서 SFP 부분에 있던 값이 ebp레지스터에 들어갔다.

이제 ret을 하게 되면 ebp+4부분에 있는 0x0804854a로 가게 될 것이다. 큰 의미는 없지만 실행시켜보면.




메인으로 돌아온 것을 확인했다.


좀 길어지고 뭘하려는건지 모르게된 감이 있는데... 이렇게 함수 func의 프롤로그와 에필로그의 과정을 알아봤다.

사실 SFP가 어떻게 작동하는지 눈으로 보기 위해서 해본 실험이었다.

결론은 SFP는 ret명령이 실행되면서 이전에 실행되던 함수로 돌아가려고 할 때 ebp를 저장해놓는 백업용도로 사용하는 것 같다.



그리고.... 이게 포스팅을 하게되면서 알게 된 사실인데 부끄럽지만 저 프로그램을 일부러 취약하게 만들었는데  우분투에서 컴파일하니 취약점이 사라져버렸다.

코드를 보면 SFP부분을 덮을수 있게 4바이트 영역에 8바이트 입력을 받는데 컴파일러가 스텍을 12바이트나 늘려버려서 SFP를 덮을수가 없었다.

취약한 바이너리를 가져와서 다시 시도해 보겠다.

.....


......


한참을 시도했으나 취약하게 만드는데 실패했다-_-;; 정확히는 fgets하는 과정에서 끝에 한바이트가 null로 바뀌면서 리턴을 한바이트 덮어버려... 세그폴트가 나며 꺼져버린다.. 허허 어쩔수 없이 fake ebp는 다음에 하는걸로




'해킹 > 개념 정리' 카테고리의 다른 글

쉘코드 작성법  (0) 2016.01.16
ASLR의 개념  (0) 2016.01.13
다중 파이프 페이로드  (0) 2015.11.24
GOT overwrite  (0) 2015.11.24
UPX  (0) 2015.11.24
Tags
Social

다중 파이프 페이로드

해킹/개념 정리 2015. 11. 24. 23:15

예컨데 어떤 문제에서 입력을 여러번 받는다면 하나의 파이프로는 입력을 전부 못할 수 있습니다. 운좋게 scanf만 사용한다면야 공백문자를 중간에 넣어서 구분해 줄 수 있겠지만 다른 입력함수는 공백역시 입력으로 받아버리는 경우가 많습니다.

이럴때 불편해서 실험해보고 알게된 사실인데 '|'(파이프) 를 여러개 이용해서 여러개의 인자를 프로세스로 전달할 수 있더군요.


ex) (python -c 'print "인자2";cat)|(python -c 'print "인자1"';cat)|./프로그램


위 예시와 같이 인자는 뒤에서부터 먼저 들어갑니다. 헷갈리지 않게 조심!



또한 이 방법을 이용해서 gdb에서 파이썬 파이프를 사용할 수 없었던 불편함을 조금이나마 줄일 수 있습니다.


ex) (python -c 'print "인자2"';cat)|(python -c 'print "인자1"';cat)|(python -c 'print "r"';cat)|(python -c 'print "b bp주소"';cat)|gdb 프로그램


이런식으로 특정 라인에 bp를 걸고 실행후 인자 전달까지 python을 이용해서 입력한 후 동적 디버깅을 할 수 있습니다.


'해킹 > 개념 정리' 카테고리의 다른 글

ASLR의 개념  (0) 2016.01.13
함수의 프롤로그와 에필로그에 관한 고찰 + fake ebp(실패)  (0) 2016.01.13
GOT overwrite  (0) 2015.11.24
UPX  (0) 2015.11.24
file destriptor  (0) 2015.11.24
Tags
Social

GOT overwrite

해킹/개념 정리 2015. 11. 24. 22:06

PLT(Procedure Linkage Table)

PLT는 일종의 실제 호출 코드를 담고 있는 테이블로써 이 내용 참조를 통해 _dl_runtime_resolve가 수행되고, 실제 시스템 라이브러리 호출이 이루어지게 됩니다.

 

GOT(Global Offset Table)

GOT는 PLT가 참조하는 테이블로써 프로시져들의 주소를 가지고 있습니다. PLT가 어떤 외부 프로시져를 호출할 때 이 GOT를 참조해서 해당 주소로 점프하게 됩니다.



- printf 함수 호출이 처음일 때

[printf 함수 호출]--->[PLT로 이동]--->[GOT 참조]--->[다시PLT로 이동]--->[_dl_runtime_resolve]--->[GOT 저장 후, 실제 함수 주소로 점프]

 

 

 

- printf 함수 호출이 처음이 아닐 때 (GOT에 실제 printf 주소가 저장되어 있음)

[printf 함수 호출]--->[PLT로 이동]--->[GOT 참조] ===> printf 함수로 점프




--------------------------------------------------------------------------------------------출처: http://bbolmin.tistory.com/33


정리하면 함수가 한번도 실행되기 전 GOT에는 PLT가 GOT로 점프하는 명령어의 바로 다음 주소가 들어있습니다. 따라서 그냥 jmp 뒤에 PLT 안의 명령어들을 쭉 실행하는 것과 같겠죠. 그러면 _dl_runtime_resolve라는 함수를 호출하게 되는데 이 함수가 실행되고 나면 GOT에 실제 함수의 주소가 들어가게 됩니다. 따라서 다음부터는 plt-got-함수 순서로 바로 함수를 호출할 수 있게 되는 것이지요.



이런 과정은 그냥 GOT와 PLT에 대한 이해를 돕기 위한 것일 뿐이고 GOT overwrite에서 중요한 점은 PLT가 GOT의 주소를 상수로 가지고 있기 때문에 GOT의 주소는 변하지 않는다는 것, 그리고 _dl_runtime_resolve 함수에 의해 변경되는 부분이므로 공격자에 의해서도 변경될 수 있다는 것입니다. GOT의 값을 바꿔주면 프로그램 내에서 해당 함수가 호출될 때 실행되지 않고 내가 원하는 주소로 점프하도록 할 수 있습니다.

'해킹 > 개념 정리' 카테고리의 다른 글

ASLR의 개념  (0) 2016.01.13
함수의 프롤로그와 에필로그에 관한 고찰 + fake ebp(실패)  (0) 2016.01.13
다중 파이프 페이로드  (0) 2015.11.24
UPX  (0) 2015.11.24
file destriptor  (0) 2015.11.24
Tags
Social