pwn刷题记录 1
注:可用目录快速导航。
前言
虽然算法题依然重要,但我想玩点CTF以更好提升自己。
前不久我开始探索WEB安全,但遗憾的是我忘记了在我的博客上记录学习的过程。
目前,我更对自己的定位是web&&pwn。
同时,我也希望通过记录自己的学习和挑战过程来培养一种良好的习惯。
以下从buuctf开始。
test_your_nc1
2023-08-23
**1.**操作
一个在搭建好环境后就可以完成的入门题。
启动靶机,打开ubuntu,terminal,直接输入nc去连接
nc node4.buuoj.cn 29757 |
**2.**获取flag
输入ls,看到了flag,输入cat flag,得到flag。
rip1
2023-08-24
**1.**分析:
先把vuln放在ubuntu的桌面,在桌面打开terminal,输入“checksec vuln”来检查它是否受保护。
把vuln在ubuntu中拖入terminal进行玩耍,他会要求输入一些东西,
比如输入一个123试试,感受一下它的交互。
将附件vuln拖入windows的IDA分析,需要注意的函数有:main函数 和 列表中main下面的fun函数
int __cdecl main(int argc, const char **argv, const char **envp) |
main函数中,我们注意gets函数是一个危险函数,它对字符串的输入是可以无限长,然后溢出的,而溢出正是pwn的主线。
其中s数组是关键,我们点开s进行查看。
-000000000000000F s db ? |
我们会看到这样一段s数组的存储空间,从1到15编号,因此我们需要输入15个字符就可以把数组s填满。
另外,在main的IDA视图开头,我们看到了
push rbp |
说明rbp被先压进去了,我们需要把rbp也填充满。
int fun() |
在此函数中,/bin/sh是个经典shell,是我们获取控制权的关键。
分析完毕,开始编写代码
**3.**python脚本的编写
在ubuntu中使用vscode
创建一个文件夹,创建文件1.py
整理后如下:
#使用pwntool工具,*表示调入所有内容。 |
另外,如果是执着于本地调试,我们可以这样:
from pwn import * |
注:
- **RSP (Stack Pointer)**:
- 总是指向栈的顶部。
- 每次调用函数、压入数据或其他需要使用栈的操作时,
RSP
都会相应地调整。 - 例如,每次执行
PUSH
指令,RSP
都会减去所需的字节数(在x86_64中通常是8字节),因为栈在低地址方向上增长。
- **RBP (Base Pointer)**:
- 用作帧指针,在函数调用中,它常常用来作为当前函数的局部变量和参数的基准点。
- 在函数调用的开头,通常会看到
RBP
被设置为RSP
的当前值,这标志着函数的开始。 - 使用基指针可以更容易地访问函数的局部变量和参数,因为它们通常都是基于
RBP
的固定偏移。
**4.**获取flag
输入ls,发现flag,输入cat flag,完成flag的获取。
warmup_csaw_2016 1
20230825
这一题与上一题很像。
1.尝试:
在ubuntu中,checksec,拖入terminal交互尝试,
2.分析:
在windows中,拖入IDA,F5进入main函数伪代码。
__int64 __fastcall main(int a1, char **a2, char **a3) |
可以看到,s数组的定义后面标注了rsp是0h开始。
可以突破的点照样是gets(),只不过这次是在return。
点入函数sub_40060D查看,它在控制台执行了输出flag的命令,这肯定是关键。
int sub_40060D() |
注意,这个函数的名称也在代表他的地址:0x40060D。
另外尝试理解这一句,可以问问chatGPT,作用是将sub_40060D的地址写到字符串s。
sprintf(s, "%p\n", sub_40060D); |
查看变量v5,
-0000000000000040 var_40 db 64 dup(?) |
var_40,可以知道它占位40h,也可以看后面十进制是64位。
另外,var代表variable,多变的,一般表示变量。
3.攻击
ubuntu中打开terminal,输入code,打开vscode。
可以利用上题的代码稍作修改,经典五行。
|
输出如下。
>-Warm Up-
>WOW:0x40060d
>>flag{bf2fb88a-c858-488b-ab42-50fe90c44d60}
>>timeout: the monitored command dumped core
>>[*] Got EOF while reading in interactive
ciscn_2019_n_1 1
0825
这次我们省略一些作用不太大的流水线操作,直接进入IDA分析
1.分析函数
IDA打开文件,进入main函数,发现有一个func()函数引起了我们的注意。
在func函数中,我们注意到return system(“cat /flag”);是我们所需要的,
而gets函数正中我们溢出攻击的下怀。
于是思路出现:
使v2变成浮点数11.28125,来执行return system(“cat /flag”);
或
直接溢出使return system(“cat /flag”);执行
int func() |
2.分析变量
双击v1,我们会看到
-0000000000000030 ; D/A/* : change type (data/ascii/array) |
注意:在汇编语言中,db
是 “define byte” 的缩写,用于定义一个或多个字节的数据。
dup
是 “duplicate” 的缩写,常用于汇编语言中表示重复某个值多次。
dd
是 “define doubleword” 的缩写。一个doubleword通常表示4字节(或32位)的数据。
问号表示每个字节的值是未指定的。
显示的阴影部分在var_30,而下一个变量v2在var_4位置
即v1数组从30h开始,到4h。
30h-4h=2Bh=44b,刚好是后面的44b。
即,v1占了44位,而v2占了4位。
剩下的“s”即rbp,占8位
“r”为return,占8位。
3.编写脚本
第一种方法:使v2变成浮点数11.28125,来执行return system(“cat /flag”);
填满v1之后就是v2了,直接要求的数字,满足 v2 == 11.28125 的判断式
from pwn import * |
第二种方法:直接溢出使return system(“cat /flag”);执行
在func函数视图单击空格,会出现
.text:00000000004006BE mov edi, offset command ; "cat /flag" |
注意此处的00000000004006BE地址,就是我们要的返回地址。
分号后面有一句 “cat /flag”,这是注释,说明执行这一句。
注:
1.在 x86_64 架构中的 calling convention,EDI
寄存器(或其64位版本 RDI
)经常用于传递函数的第一个参数。
2.在 x86_64 架构中,当调用某些函数时,EAX
寄存器用于指示要使用的参数数量,特别是变长参数的函数。在这种情境下,这行代码可能是为了告诉系统函数 “_system” 不带额外的参数。
3.call _system调用了system函数,用于执行命令。
4.jmp short loc_4006D9表示调到某地址,我们双击它,可来到
.text:00000000004006D9 loc_4006D9: ; CODE XREF: func+57↑j |
这三个指令通常出现在函数的结尾。让我们逐一解释它们的作用:
.text:00000000004006D9 nop
nop
指令是 “No Operation” 的缩写,意味着它不执行任何操作。它的主要用途是为了代码对齐或在二进制中填充空间。有时,nop
也在调试或分析时被使用,因为你可以安全地替换它而不改变程序的功能。.text:00000000004006DA leave
leave
指令是函数结束时常见的指令,它执行两个主要操作:- 将
RBP
寄存器的值加载到RSP
寄存器。这将堆栈指针RSP
重置为它在函数入口时的位置。 - 之后,它从堆栈中弹出一个值到
RBP
寄存器。这恢复了调用函数时RBP
寄存器的原始值。
总的来说,这实际上是
mov rsp, rbp
和pop rbp
两个指令的组合。- 将
.text:00000000004006DB retn
retn
指令从堆栈中弹出一个返回地址并跳转到那个地址,这结束了当前的函数调用,程序的控制权返回到调用此函数的代码。在 x86 架构中,retn
和ret
是等价的。
结合起来看,这些指令在函数结束时恢复了原始的堆栈和帧指针,并返回到调用此函数的地方。
python脚本代码如下:
from pwn import * |