pwn题解集 2(持续更新中)
get_started_3dsctf_2016(*)
9.2,9.3
难题真多,先做个简单的(然后发现还是好难!!!但是方法是真的多,可易可难,好题!)
分析:
checksec
Arch: i386-32-little |
有NX,i386,32位
方法一:
from pwn import * |
方法二:
from pwn import * |
方法三:
from pwn import * |
反思
1.如何找到写'/bin/sh'
的地址?
1.执行题目的可执行文件,如./vuln
2.寻找该文件的当前进程的PID,pgrep -f vuln
,假设返回值12345
3.查找可写地址,pmap 12345 | grep rw
,搞定。
🚀 mprotect函数简介
mprotect
是一个系统调用,主要用于改变内存区域的保护属性。在Linux环境下,它的原型如下:
|
🎯 主要参数
addr
: 要更改的内存区域的起始地址。通常,这个地址必须是系统页大小的倍数(一般是4KB)。len
: 需要更改的内存长度。prot
: 指定新的保护属性。它是以下标志的组合:PROT_NONE
: 不可访问。PROT_READ
: 可读。PROT_WRITE
: 可写。PROT_EXEC
: 可执行。上文提到的
0x7
实际上是一个组合值,我们可以将其分解为多个权限标志来看。🔍0x7
在二进制中表示为111
,其中:- 最低位(第0位)代表
PROT_EXEC
,表示可执行权限。 - 中间位(第1位)代表
PROT_WRITE
,表示可写权限。 - 最高位(第2位)代表
PROT_READ
,表示可读权限。
所以,
prot
为0x7
时,意味着这段内存区域同时具有读、写、执行的权限。😄🌈- 最低位(第0位)代表
🎉 返回值
成功时,返回0;失败时,返回-1,并设置errno
。
善用GDB调试,debug日志等。
🧐 代码解析
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
- shellcraft.sh(): 这是
pwntools
的shellcraft
模块提供的函数,它会生成一个基本的shellcode,通常用于弹出一个shell。这个shellcode默认是用汇编语言描述的。 - asm(): 这是
pwntools
的一个函数,它的作用是将给定的汇编代码编译成二进制格式的shellcode。在上面的代码中,它将shellcraft.sh()
生成的汇编shellcode转换成了二进制格式。 - arch=’i386’, os=’linux’: 这些是
asm
函数的参数,用于指定目标架构和操作系统。在此例中,目标架构是i386
(32位x86架构),操作系统是Linux。
🎉 结果
得到一个可以在32位Linux系统上运行的、用于弹出shell的二进制shellcode。
- shellcraft.sh(): 这是
ciscn_2019_s_3
9.1
有点难,很多没搞懂,搞懂了再来更新
from pwn import * |
bjdctf_2020_babystack
0831
from pwn import * |
0830
ciscn_2019_n_8
今天的题目咋这么草率
1.exploit
from pwn import * |
2.反思
1.payload有两种表示方法,第一种纯用数字1填数组,第二种注意一个整数占四位,要乘4
2.checksec开启了几乎所有保护,但是通过IDA看出可以纯纯通过要求输入来搞定,更简单了。
jarvisoj_level2
难度骤降,受不了
1.exploit
from pwn import * |
2.反思
1.注意32位与64位的区别
2.sys的地址实际为“call system”
3.IDA注意shift+f12查看字符串的技巧
pwn1_sctf_2016 1
0826
1.分析
IDA打开,vuln函数,代码比较长,就不直接展示了。
捕捉到关键信息如下:
char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF |
由于伪代码会出现很多乱七八糟的东西,这个时候就需要选择性忽略。
另外,看代码属于静态,我们还可以去进行动态的调试,来快速理解题目功能。
在ubuntu的terminal中,安装C++文件依赖:
sudo apt-get install lib32stdc++6 |
执行文件,输入
youyouyouIII111 |
返回
So, youyouyouyouyouyou111 |
发现“I”全都替换成了“you”。
说明我只要输一字符“I”就可以获得三字符的“you”,
因此需要尝试爆破的话,距离ebp60位,只需要20个“I”就可以解决。
此外,我们ebp是4位,需要再来一个“I”和一个其他字符或四个其他字符。
另外,发现一个后门函数是get_flag:
int get_flag() |
找到:
.text:08048F13 mov dword ptr [esp], offset command ; "cat flag.txt" |
地址为:0x8048F13
理论可行,实操开始。
2.实操
Ubuntu中打开code,代码如下
from pwn import * |
over。
jarvisoj_level0 1
0827
前面做了这么多,这题有点过于老套了。
1.分析
IDA:
注意vulnerable_function和callsystem这两个函数。
其中vulnerable_function如下:
ssize_t vulnerable_function() |
很经典,考虑 80h buf + 8h rbp
另外callsystem中轻易获得唤起控制台地址。
2.代码
from pwn import * |
[第五空间2019 决赛]PWN5 1
0827
1.尝试
使用checksec:
$ checksec pwn |
注意到有Canary,溢出有保护。
NX开启,说明栈中数据没有执行权限。
运行程序,初步尝试。
2.分析
用IDA32位,打开,关键部分如下:
char nptr[16]; // [esp+4h] [ebp-80h] BYREF |
注:BSS(Block Started by Symbol)代表了程序未初始化的全局变量和静态变量的存储区域。
3.代码
方法一:篡改unk_804C044的值
以下确定输入的字符串偏移量:
$ './pwn' |
通过这个测试,我们知道abcd被存储在参数列表中第十个参数的位置。(a的ascii码是61)
IDA中可以找到unk_804C044的地址就是0x804C044
.bss:0804C044 dword_804C044 dd ? ; DATA XREF: main+77↑o |
printf函数的一个漏洞就是可以借助**%n**写入数据。
python代码如下:
from pwn import * |
方法二:将atoi函数直接改为执行system函数
python代码如下:
from pwn import * |
解释一下一些知识盲区:
atoi_got_addr = elf.got['atoi']
:- 这行代码从当前程序的**全局偏移表(GOT)**中提取了
atoi
函数的地址。GOT是一个在运行时由动态链接器维护的表,用于存储每个外部函数(如来自共享库的函数)的实际地址。 - 当程序第一次调用一个外部函数时,它实际上是通过GOT的对应条目来调用的。在程序第一次调用该函数后,动态链接器将填充该函数的实际地址到GOT。
- 由于GOT存放在已知的地址,并且可能在程序的生命周期中被修改,因此它经常成为二进制漏洞利用的目标。
- 这行代码从当前程序的**全局偏移表(GOT)**中提取了
system_plt_addr = elf.plt['system']
:- 这行代码从程序的**过程链接表(PLT)**中提取了
system
函数的地址。PLT是一个存放跳转指令的表,它在程序第一次调用一个外部函数时使用。 - 当程序尝试调用一个还未解析的外部函数时,它首先跳转到PLT中的对应条目。然后,PLT条目将使用GOT中的信息来跳转到实际的函数地址(或者调用动态链接器来解析它)。
- 这行代码从程序的**过程链接表(PLT)**中提取了
format_string_offset = 10
:- 这声明了一个变量,表示我们预计格式化字符串开始的位置是第10个参数。这通常是通过测试和实验来确定的。
payload = fmtstr_payload(format_string_offset, {atoi_got_addr: system_plt_addr})
:fmtstr_payload
是pwnlib
库中的一个函数,用于生成格式化字符串攻击的payload。- 第一个参数(
format_string_offset
)告诉函数格式化字符串在参数列表中的位置。 - 第二个参数是一个字典,描述了应修改哪些地址及其新的值。在这里,我们想将
atoi
的GOT条目改为system
函数的PLT地址。换句话说,我们想在下次程序调用atoi
函数时实际上执行system
函数。
该攻击涉及到重写GOT中的atoi
函数地址,使其指向system
函数或其他恶意代码。这样,下次程序尝试调用atoi
时,它实际上会执行攻击者选择的代码。
ciscn_2019_c_1 1
20230828
一个ROP。
代码先行。
1.python代码
from pwn import* |
2.思路
TERMINAL:
弹出一个界面,有三个选项。选1,会让你输入一些东西;选2,没啥卵用;选3,直接退了。
可见,输入这个部分可能是突破口。
IDA:
于是我们注意encrypt函数,不要被它的逻辑吸引了,
主要就是个gets和puts这些复古且不安全的函数需要被我们利用。
而数组s是其中的主角。puts可以利用格式化字符串漏洞。
利用rdi进行ROP攻击。
3.反思
1.代码模块化是一种新的尝试。注意增加代码可读性,注释里也全是信息和知识点。
2.ROP中,puts之后会造成栈不对齐,我们需要重新栈对齐,用一个简单的函数来return修正(它会调整rsp)
3.checksec也是一种提示,比如这题NX enabled,一般用ROP绕过。
4.注意ROP链几个重要细节:每一个函数、寄存器结束之后都会有个ret;plt是被ret执行,用来执行got的。