[SWPU2019]Web4
[SWPU2019]Web4
考点
PDO场景下的SQL注入
SQL盲注
代码审计
wp
PDO多语句执行
PHP数据对象(PDO)扩展为PHP访问数据库定义了一个轻量级的一致接口 PHP连接MySQL数据库的方式:MySQL、Mysqli、PDO

可以看到Mysqli和PDO是都是支持多语句执行的
Mysqli通过multi_query()函数来进行多语句执行,普通的mysqli_query()函数只执行单个语句


PDO中通过query()函数同数据库交互

PDO默认支持多语句查询,如果php版本小于5.5.21或者创建PDO实例时未设置PDO::MYSQL_ATTR_MULTI_STATEMENTS为false时可能会造成堆叠注入
如果想禁止多语句执行,可在创建PDO实例时将PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为false
new PDO($dsn, $user, $pass, array( PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))
MySQL预处理
MySQL官方将prepare、execute、deallocate统称为PREPARE STATEMENT
预制语句的SQL语法基于三个SQL语句:
PDO预处理
PDO预编译执行过程分三步: prepare(SQL) 编译SQL语句 bindValue(param, $value) 将value绑定到param的位置上 execute() 执行
PDO分为模拟预处理和非模拟预处理。 模拟预处理是防止某些数据库不支持预处理而设置的,在初始化PDO驱动时,可以设置一项参数,PDO::ATTR_EMULATE_PREPARES,作用是打开模拟预处理(true)或者关闭(false),默认为true。PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行。
非模拟预处理则是通过数据库服务器来进行预处理动作,主要分为两步:第一步是prepare阶段,发送SQL语句模板到数据库服务器;第二步通过execute()函数发送占位符参数给数据库服务器进行执行。
如果说开启了模拟预处理,那么PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行;如果我这里设置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不会模拟预处理,参数化绑定的整个过程都是和Mysql交互进行的。
模拟预处理
非模拟预处理代码
两段代码的区别在于,非模拟预处理代码在 $username = '1'; 前多了一行 $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
预处理下的安全问题
模拟预处理下
正常请求 http://127.0.0.1/pdo/5.php?id=1&field=username 结果如下: array(2) { ["id"]=> string(1) "1" ["username"]=> string(5) "admin" }
但是field参数可控,可以执行多条语句 http://127.0.0.1/pdo/5.php?id=1&field=username from users;select username,password 结果如下: array(2) { ["id"]=> string(1) "1" ["username"]=> string(5) "admin" } array(2) { ["id"]=> string(1) "4" ["username"]=> string(5) "test4" }
当设置$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);时,也可以达到报错注入效果
http://127.0.0.1/pdo/5.php?id=1&field=username from users where (1 and extractvalue(1,concat(0x7e,(select(database())),0x7e)));%23
结果:
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~user~' in......line 20
非模拟预处理时,同样的field字段可控,这时多语句不可执行,但是当设置$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);时,也可进行报错注入
http://127.0.0.1/pdo/5.php?id=1&field=username from users where (1 and extractvalue(1,concat(0x7e,(select(version())),0x7e)));%23
结果:
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~5.7.26~' in......line 20
wp
正常请求

在username处加上单引号就会报错,但是加上 '; 就返回正常

由于过滤了select,if,sleep,substr等大多数注入常见的单词,但是注入又不得不使用其中的某些单词。那么在这里我们就可以用16进制+mysql预处理来绕过。

脚本跑不出来
结果是glzjin_wants_a_girl_friend.zip
下载,然后进行代码审计,典型的MVC框架,先看一下核心配置,找到路由控制
假设GET的输入为?r=Login/Index,这段代码就去请求LoginController中的actionIndex方法
然后看一下控制器文件,总共三个。在BaseController.php中发现extract函数,可能存在变量覆盖,接着找可控点,在UserController.php发现其actionIndex函数是完全可控的,其调用的视图为userIndex.php,然后在userIndex.php发现存在文件包含,结合上面的变量覆盖就可以实现任意文件读取



favicon.ico和flag.php在同一个路径下,把favicon.ico改成flag.php即可 最终payload ?r=User/Index&img_file=/../flag.php
小结
SQL注入要注意可以执行多条语句的情况,使用16进制+mysql预处理
最后更新于