CVE-2021-29083漏洞复现

复现群晖DSM某服务漏洞

Posted by X1ng on September 17, 2021

搜集相关信息

搜了一大圈只能找到同一句漏洞描述,随便摘了一个

https://www.cvedetails.com/cve/CVE-2021-29083/

Improper neutralization of special elements used in an OS command in SYNO.Core.Network.PPPoE in Synology DiskStation Manager (DSM) before 6.2.3-25426-3 allows remote authenticated users to execute arbitrary code via realname parameter. Publish Date : 2021-04-01 Last Update Date : 2021-04-07

可以得到以下信息

  1. 漏洞存在于6.2.3-25426-3之前的版本
  2. 这是一个命令注入漏洞
  3. 可能与”SYNO.Core.Network.PPPoE”字段有关
  4. 可能与”realname”字段有关

固件下载

在官网下载存在漏洞的固件版本以及漏洞修复后的固件版本

DSM_DS918+_25426.pat

DSM_DS918+_25556.pat

找到漏洞文件

binwalk解压一下

binwalk -Me  DSM_DS918+_25426.pat
cd _DSM_DS918+_25426.pat.extracted

查找可能存在漏洞的文件

find . -type f | xargs strings -f | grep "SYNO.Core.Network.PPPoE"

找到了./usr/syno/synoman/webapi/SYNO.Core.Network.lib

是json格式的配置文件,在CyberChef上美化一下格式

找到”SYNO.Core.Network.PPPoE”字段

    "SYNO.Core.Network.PPPoE": {
        "allowUser": [
            "admin.local",
            "admin.domain",
            "admin.ldap"
        ],
        "appPriv": "",
        "authLevel": 1,
        "lib": "lib/libwebapi-PPPoE.so",
        "maxVersion": 1,
        "methods": {
            "1": [
                {
                    "get": {
                        "allowDemo": true,
                        "grantByDefault": true
                    }
                },
                {
                    "set": {
                        "grantByDefault": true
                    }
                },
                {
                    "list": {
                        "allowDemo": true,
                        "grantByDefault": true
                    }
                },
                {
                    "connect": {
                        "grantByDefault": true
                    }
                },
                {
                    "disconnect": {
                        "grantByDefault": true
                    }
                }
            ]
        },
        "minVersion": 1,
        "priority": -10
    }

其配置文件的格式参考cq674350529师傅的ppt,api有对应的so文件,在so文件中不同的virsion(例如1、2)和methods(例如get、set)有对应的handle函数,查找对应的so文件

find . | grep "lib/libwebapi-PPPoE.so"

找到./usr/syno/synoman/webapi/lib/libwebapi-PPPoE.so文件,应该是存在漏洞的文件

然而不知道是否我的理解有误,在文件系统中全局搜索”realname”字符串却没有找到有用的信息

分析漏洞成因

ida打开,搜索”SYNO.Core.Network.PPPoE”字符串

通过字符串的交叉引用找到对应api的函数

其结构体为

struct api{
    char * apiname;
    size_t virsion;
    char * method;
    func * handler;
}

lib文件中不同的api、viersion、method都对应了handler文件,但是不知道哪一个才是真正存在漏洞的函数

可以安装bindiff来比较补丁前后的函数变化

bindiff安装参考BinDiff安装使用教程 - 诸子流 - 博客园 (cnblogs.com)

在用ida分析后将两个i64文件进行differ

但是经过分析补丁前后两个文件差别不大,只是在一些函数上加上了canary检测

不能直观地从diff中找到漏洞修补点,只能先分析漏洞文件

根据漏洞描述,存在漏洞的api应该是”SYNO.Core.Network.PPPoE”,所以漏洞应该在get、set、list、connect四个method对应的函数中;

能够造成命令注入漏洞,set函数的可疑性较高,connect次之,毕竟get和list几乎没有输入的字段,先着重分析set对应的函数(其实漏洞就在该函数中)

进入InterfaceHandler<syno::network::PPPoEInterface>::Set函数

整体逻辑是先判断一些字段参数是否存在,然后是如下的主要部分

按照函数逻辑,应该是判断如果某两个字段一样的话调用SYNO::APIResponse::SetSuccess函数来返回true的响应,否则就会调用InterfaceHandler<syno::network::PPPoEInterface>::Set(该函数虽然与上面的set函数名称相同,但是参数不相同,是该类中的重载函数)

进入这一个set函数,主要逻辑如下

这里全是指针的方式调用的,可能是通过虚表调用虚函数,经过动态调试分析可以发现此处调用的函数是libwebapi-Network-Share.so文件中的syno::network::PPPoEInterface::SetData函数,进入该函数继续分析

发现此处又调用了一个函数指针,动态调试可以确定函数为syno::network::PPPoEInterface::LoadData

不过并不很重要,主要是看下面的逻辑,后面调用了syno::network::PPPoEInterface::Checksyno::network::PPPoEInterface::Apply函数,发现十分眼熟,看了一下陈前师傅的ppt

是同一个调用链,于是顺着ppt的思路在syno::network::PPPoEInterface::Apply函数中找到调用PPPoEConfigSet函数的指令,在文件系统中搜索”PPPoEConfigSet”字符串

找到实现其功能的so文件:libsynonetsdk.so.6,分析PPPoEConfigSet函数

其主要逻辑与ppt中描述的一致,应该是同一个漏洞成因,只对”USER”字段的特殊字符进行了过滤而忽略了”MTU”字段,只不过触发漏洞的方式有所不同,ppt的漏洞中从EZ-internet套件调用漏洞函数,CVE-2021-29083从 “SYNO.Core.Network.PPPoE” api调用漏洞函数

该函数会按照”key=value”的格式保存数据到 /etc/ppp/pppoe.conf文件中,而在 /usr/sbin/pppoe-start脚本中将该conf文件作为配置文件,并调用了 /usr/sbin/pppoe-connect

只要将shell命令注入到 /etc/ppp/pppoe.conf文件中,在PPPoE服务连接的时候就可以命令注入

而 /usr/sbin/pppoe-connect 从配置文件中提取”USER”和”MTU”字段作为参数,可以通过插入” ` “字符进行命令注入

搭建实验环境

参考之前的发过的CVE-2021-27647漏洞复现搭建群晖虚拟机进行复现

触发漏洞

找到与pppoe有关的服务抓包分析

可以找到 控制面板->网络->网络界面 中有PPPoE选项

右键编辑,可以用burp抓到请求报文,报文的格式与逆向so文件中需要的格式十分符合

随意输入一些账号密码后确定,经过分析发现单击确定后会先发送一个获取公钥的请求获取token、公钥等数据

接收到公钥后通过前端将所有参数按照一定格式加密后,再将加密后的密文发送到后端

之前逆向so文件可以知道此处会被插入命令的字段只有用户账号和MTU值两项,但是用户账号经过了字符过滤,无法进行命令注入,而MTU值又在前端限制了只能输入数字

所以若要触发命令必须向MTU值的字段插入命令,想到了三种方案:

  1. 修改js代码,让只能输入数字的限制失效
  2. 对js进行逆向分析,找到加密方式,再伪造前端请求发包
  3. 对js进行调试,在加密前通过js控制台将MTU的值修改为需要注入的命令

然而由于前端太菜,既找不到限制只能输入数字的js代码,又逆向不出加密方法,并且复现漏洞的目的应该是学习了解设备的攻击面,所以采用最简单的方法3,等之后有时间了再慢慢研究一下其加密逻辑

学习一些js调试的基本操作,找到一篇介绍使用chrome调试的文章:js逆向技巧分享

但是chrome设置代理似乎比较麻烦,参考该教程用火狐浏览器调试也是大同小异

打开PPPoE配置界面,f12在前端代码中搜索”确定”

找到对应的html标签,右键单击->打断点于…->节点删除时

接着修改一下配置单击确定就可以停在断点处了,在调试器右侧的调用堆栈中单击”展开行”选项即可看到所有函数调用堆栈

在调用堆栈里找到onEncryptRequestAPI函数

美化一下源代码

该函数就是对配置信息进行加密的函数,再加密后将密文发送到后端,所以在这个函数开始处设置一个断点

恢复运行后再重新修改配置跑一遍js代码,并一直单步运行到d = SYNO.Encryption.EncryptParam函数,步入该函数,查看g变量

可以发现 g.configs 的内容正是我们配置的内容,在控制台修改这个变量的值

g.configs= "[{\"ifname\":\"pppoe\",\"real_ifname\":\"eth0\",\"username\":\"aaaaa2332a3\",\"is_default_gateway\":true,\"mtu_config\":\"`reboot`\"}]"

修改成功后恢复运行,但是发现系统并没有重新启动,应该是有些地方出现了问题

ssh连上去看一下/etc/ppp/pppoe.conf文件的内容

可以看到此时配置文件中MTU只有”`reboot”字符串,也就是我们修改的字符串并没有完整的写入配置文件中

再结合陈前师傅的ppt可以找到设置配置文件的处理逻辑是syno::network::PPPoEInterface::Check函数对用户传入的参数检查后复制到该类的成员变量中,再由syno::network::PPPoEInterface::Apply函数将成员变量中的配置信息保存到配置文件

所以上面执行reboot失败的原因是复制MTU参数时的长度存在限制,只能输入7字节的字符串

只能输入一些短小的命令进行概念验证,在控制台输入

g.configs= "[{\"ifname\":\"pppoe\",\"real_ifname\":\"eth0\",\"username\":\"aaaaa2332a3\",\"is_default_gateway\":true,\"mtu_config\":\"`id>aa`\"}]"

打开burp代理后再发送可以抓取poc报文

在发送报文后再发送连接报文就成功执行命令了

执行成功,根目录下多了一个aa文件

参考资料

CVE-Details

POC2019

BinDiff安装使用教程 - 诸子流 - 博客园 (cnblogs.com)

CVE-2021-27647漏洞复现

js逆向技巧分享