Pwnable!!

0ctf 2017 babyheap 문제를 푸는데, chunk의 size를 조작해서 free()하고 재할당하려는데 자꾸 오류가 발생한다 ㅡㅡ;


*** Error in `/home/0ctf_BabyHeap2017/0ctfbabyheap': free(): invalid next size (fast): 0x0000555555757040 ***


위와 같은 오류가 발생하는데... 대체 뭐가 문제인거지 ㅁㄴㅇㄹ 하면서 열심히 찾아보면서 원인을 알아내려했는데

나와 같은 에러때문에 고민하시는 분이 없는건지, 정확한 자료가 나오지않았다가 방금 갑자기 깨달아서 글로 남긴다ㅇ



그냥 에러 그대로였다. invalid next size다. 

allocated chunk와 freed chunk가 있을 때 인접한 앞/뒤 chunk의 주소를 prev_size와 size로 계산하게 되는데, 

chunk를 free()하게 되면 next chunk(chunk address + size)로 접근해서 prev_inuse bit를 바꿔주게된다. 그런데 이 size를 조작하게 되면 다음 chunk를 찾지못해서 오류가 발생한다.


즉, 위 에러가 뜬 이유는 chunk를 free()하고 (해제한 chunk + 조작된 size)에 있는 chunk의 prev_inuse bit를 바꿔주러갔더니? 

웬걸, 아무것도 없넹??? 해서 발생한것이였다...;



그러므로 size조작을 할때 위와 같이, next chunk의 위치를 제대로 잡을 수 있는 size로 조작해주는게 좋다.

끝!



아 참고로 이분이 heap에서 발생하는 에러에 대해서 pseudo-code잘 정리해두셨길래 링크남기고 글을 끝낸다.

http://dokydoky.tistory.com/460


'Pwnable!!' 카테고리의 다른 글

[main_arena_offset] libc파일에서 main_arena 오프셋 구하기  (0) 2019.01.27
[C] read 함수  (0) 2019.01.21
gdb에서 __malloc_hook / __free_hook 주소 찾기  (0) 2018.10.06
pwntools 사용해서 ssh 연결  (0) 2018.07.23
setvbuf란?  (0) 2018.07.10

x/x &__malloc_hook

p/x &__malloc_hook


x/x &__free_hook

p/x &__free_hook



가끔 잊어먹어서 적어놓는다.

'Pwnable!!' 카테고리의 다른 글

[C] read 함수  (0) 2019.01.21
Heap Overflow Chunk Size 조작 (feat. invalid next size )  (0) 2018.10.09
pwntools 사용해서 ssh 연결  (0) 2018.07.23
setvbuf란?  (0) 2018.07.10
bash drop privileges  (0) 2018.07.06
1
2
3
= ssh(user='myria',host='sherlock.myria.break',port=1234,password='asdf')
= s.process("./binary")
cs

위와 같이 하면 ssh서버에서 process로 바이너리 실행해서 exploit할 수 있다.



1
2
= ssh(user='myria',host='sherlock.myria.break',port=1234,password='asdf')
conn = s.connect_remote('localhost'1235)
cs


remote접속도 마찬가지로 저렇게 가능.




ssh()의 경우에는 말 그대로 ssh 리모트 연결을 해주는 함수인데, 포트와 패스워드는 디폴트값으로 22와 guest가 설정되어 있다.

ssh로 연결해줄때, 유저네임과 호스트명은 필수지만 포트와 패스워드는 필수가 아니다. 하지만 원하는 연결을 하기 위해서는 이 두 가지도 설정


그리고 배열을 통해 whoami등의 명령의 결과를 알아낼 올 수 있는데

이는 ssh연결을 하는 순간, 리스트에 있는 명령어들의 결과를 배열로 저장해서 필요에따라 보여주는 것

또한 run()을 이용해서 /bin/sh 등과 같은 프로그램을 리모트에서 실행하는 것도 가능하며 sendline()으로 커맨드를 실행하는 것도 가능.


1
2
3
4
5
6
7
8
9
10
11
from pwn import *
 
shell = ssh("fd""pwnable.kr", port=2222, password="guest")
print(shell['whoami'])
 
sh = shell.run('/bin/sh')
sh.sendline("echo test")
 
print(sh.recvline(timeout=3))
 
shell.close()
cs


setvbuf란?

2018. 7. 10. 15:26

bash drop privileges

2018. 7. 6. 22:21

http://shayete.tistory.com/entry/1-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%95%B4%ED%82%B9%EC%9D%B4%EB%9E%80-linux-%EA%B8%B0%EC%B4%88%EB%AA%85%EB%A0%B9%EC%96%B4-vim-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%82%AC%EC%9A%A9%EB%B2%95


https://bpsecblog.wordpress.com/2016/10/05/command_injection_02/



'Pwnable!!' 카테고리의 다른 글

pwntools 사용해서 ssh 연결  (0) 2018.07.23
setvbuf란?  (0) 2018.07.10
매직 가젯, 원샷 가젯 (64bit O / 32bit eax라면 O/ rax X)  (0) 2018.05.28
call instruction  (0) 2018.05.27
ROP gadget 찾기 (gdb-peda, rp++)  (0) 2018.05.26

※ magic gadget (onshot gadget) 이란


execve() 함수 안에 "/bin/sh"를 호출해주는 부분이 존재하는데, magic gadget 은 바로 이 부분을 지칭한다. 이 부분을 찾기 위해서는 라이브러리에서 찾아보면된다.

우리는 libc 기본 주소만 알고 있다면 offset을 통해 해당 부분을 호출하여 쉘을 얻을 수 있다. 그렇기에 "magic gadget" 이라고 한다. 하지만 magic gadget은 로컬과 xinetd로 돌아가는 서비스에서만 가능하다.


oneshot gadget 찾아주는 툴 : https://github.com/david942j/one_gadget

사용법 : http://tribal1012.tistory.com/143



참고로 위의 oneshot gadget을 찾아주는 툴은  64bit에서만 쓸 수 있다. 32bit에서 쓰려고해서 써보니...

리턴된 주소에서 execve에 들어갈 인자를 edi에 넣어주는 등 64bit기반으로 동작한다 ㅡㅡ; 게다가 애초에 매직 가젯은 32bit에서 안써진다고 한다.

후... 안써져서 괜히 삽질만 열심히 했네 ^^

64bit는 rip를 기준으로 주소를 불러올 수 있기 때문에 바로 부를수 있으나 32bit는 그게 안된다나 뭐라나... 

해커스쿨에 이에 관한 질문과 답변한 내용이 있더라... (http://www.hackerschool.org/HS_Boards/zboard.php?id=QNA_system&no=1828)


는 아니다 32bit도 된다. 되는 것이 있다.



이렇게 eax나 esp를 기준으로 해서 작동하는 것들은 다 oneshot 가젯인데, 32bit에서 작동되는 oneshot가젯이고



rax나 rsp 등을 기준으로 작동하는 execve("/bin/sh")는 64bit에서 작동하는 oneshot가젯이다.


'Pwnable!!' 카테고리의 다른 글

setvbuf란?  (0) 2018.07.10
bash drop privileges  (0) 2018.07.06
call instruction  (0) 2018.05.27
ROP gadget 찾기 (gdb-peda, rp++)  (0) 2018.05.26
linux에서 lib64의 base address 확인 및 팁  (0) 2018.05.24

call instruction

2018. 5. 27. 09:47

CALL (Call a Procedure)

함수 호출시 사용된다. JMP명령어 같이 프로그램의 실행 흐름이 변경되지만 JMP명령와 다른 점은 되돌아올 리턴 어드레스(CALL 다음 명령)를 스택에 저장한다는 것이다. 되돌아올 주소를 저장하기 떄문에 함수 호출 후 원래 위치로 실행 흐름을 되돌릴 수 있다. 호출한 함수가 일을 다 마치면 원래 위치에서 다시 프로그램이 실행될 수 있음을 의미한다.


call & jmp 명령의 차이

  - call 명령은 함수가 호출되기전 Return adress 인 EIP(다음명령주소)를 Stack 최상단에 넣고 함수를 호출

ROP gadget을 좀 더 효율적으로 찾기위해서 툴을 사용하는게 좋다. 

기존에는 아래와 같이 objdump와 grep을 이용해 찾아 사용했었다.


1
objdump -d echo1 | grep 'pop' -A2 --color=auto
cs


그런데 이렇게 찾는것도 한계가 있고, 잘 못찾아주는 경우가 많다.

여기서 gdb-peda에 보면 ropsearch와 ropgadget이란 기능이 있는데, 바이너리에서 gadget찾기가 용이하다.

(참고로 바이너리가 실행된 상태(run)에서만 찾는 것이 가능하다.)



ropgadget을 사용하면 자주 사용하는 유용한 gadget들을 한번에 찾아준다.

ropsearch는 지정된 gadget을 찾아주는 것인데, 이를 사용하려면 NASM을 설치해야한다.



NASM을 설치하면 위와 같이 사용가능하다. "pop ?"로 search하면 아래와 같이 주소값과 함께 출력된다.

그런데 이것도 단점이 있는것이.. libc 등에서 gadget offset을 찾으려고 gdb-peda로 실행해서 rop명령어를 쓰면


실행이 안되서 못찾는다 ㅡㅡ;;


이때는 rp++를 쓰자. https://github.com/0vercl0k/rp



Tip.

linux에서 lib64의 base address에서 첫 4byte 는 보통 "\x7fELF" or "\x7f\x45\x4c\x46"이다 그래서 vmmap을 보고 libc의 base address의 첫 4byte를 확인하고 libc인지 확인할 수 있다.


$ldd를 통해서 어떤 라이브러리를 사용하고 있는지 확인할 수 있다.


$ readelf -s /lib/x86_64-linux-gnu/libc.so.5 | grep 'system' 을 이용하여 라이브러리에서 해당 함수의 주소를 찾을 수 있다.

$ strings -a -tx /lib/x86_64-linux-gnu/libc.so.6  | grep /bin/sh 을 이용해서 libc에서 /bin/sh를 찾을 수 있다.


출처: http://gnu-cse.tistory.com/61 [GNU]


64bit ROP & 64bit BOF

2018. 5. 24. 00:28

최근 64bit BOF 및 ROP 문제를 접하게 되었는데, 그 전까지는 x86, 32bit만 다루어봐서 하루종일 삽질을 함과 동시에 대체 "왜 모두 제대로했는데... 익스중에 죽어버리는 거지?!??"하면서 계속해서 원인 파악을 위한 디버깅과 여러가지 시도 등... 굉장히 삽질을 하였지만 결국 왜인지 알 수 없었다.


그래서 64bit 관련해서 ROP, BOF를 찾아보니 32bit와는 꽤나 다른 것을 알 수 있었다..(아...)


먼저 이건 알고있던 사항이지만 32bit와는 다르게 64bit환경에서는 레지스터, 데이터의 크기가 기본 8byte이다.

또 64bit에서는 접근 주소의 범위가 첫 47bit까지이다. 만약 BOF를 일으켜 ret를 조작하려할 때, 0x00007fffffffffff보다 큰 값이 들어오면 에러가 나면서 종료되게 된다.


이제 몰랐지만 이제 알게된 내용. 64bit에서 함수 호출방식이 32bit와 다르다는 것!

32bi의 경우에는 인자가 ret 다음에 위치하게 되어, ebp를 기준으로 이를 참조하는 방식이다.

+----------------------+ |...higher addresses...| +----------------------+ | 1000 | <-- sleep() looks here for its param (on 32-bit) +----------------------+ | [return addr] | <-- where esp will be when sleep() is entered +----------------------+ | [sleep's addr] | <-- return addr of previous function +----------------------+ |...lower addresses....| <-- other data from previous function +----------------------+

And on 64-bit:

+----------------------+
|...higher addresses...|
+----------------------+ <-- sleep()'s param is in rdi, so it's not needed here
|     [return addr]    | <-- where rsp will be when sleep() is entered
+----------------------+
|    [sleep's  addr]   | <-- return addr of previous function
+----------------------+
|...lower addresses....| <-- other data from previous function
+----------------------+

We'll dive into deeper detail of how to set this up and see way more stack diagrams shortly. But let's start from the beginning!


하지만 64bit는 다르다... 64bit에서는 6개짜기의 인자가 레지스터에 들어간다.

첫번째 인자는 rdi 레지스터에 들어가고, 순서는 아래와 같다.


-----register field-----

rdi : 첫번째인자

rsi: 두번째인자

rdx: 세번째인자

rcx: 네번째인자

r8: 다섯번째인자

r9: 여섯번째인자

-------stack field-------

r10(%rsp): 일곱번째인자

r11, 0x8(%rsp): 여덟번째인자

r12 

....


[64BIT CALLING CONVENTION]

- function@plt + rdi + rsi + rdx + rcx



그래서 스택에는 인자가 따로 들어가지않는다.


bof가 나서 프로그램이 죽으면 ret instruction에서 죽게되는데(이걸 몰라서 계속 여기서 죽는데 지금까지 이유를 몰랐다.)

보통은 rsp가 잘못되거나 rsp가 가리키는 값(rip가 되는값)이 잘못되었기 때문인데...


여기서 인자를 32bit처럼 올려주고 실행하려고 하면 당연히 인자는 rdi레지스터부터 가져오므로 잘못된 호출로 죽게된다. (스택은 아무의미가 없다 ㅡㅡ;)


그러므로 함수호출전에 pop rdi 같은 가젯들을 이용해 인자를 넣어주는게 좋다.



+ Recent posts