CVE-2020-8597 pppd stack buffe
0x0 背景
pppd是一个通过串行接口连接的点对点协议(PPP)
github地址 :https://github.com/paulusmack/ppp
在ppp 2.4.2 - 2.4.8版本有一个栈溢出漏洞,我试着分析了整个过程
0x1复现整个过程
准备两台ubuntu的虚拟机(我用的virtualbox),设置启用串口功能。分别在两台虚拟机上git clone一份ppp,并回滚到有漏洞版本,编译安装。例如
git clone https://github.com/paulusmack/ppp.git
cd ppp
git checkout ppp-2.4.8
./configure
make
make install
开启服务端的md5认证(MD5-Challenge)
打开文件/etc/ppp/chap-secrets添加下面内容
admin * password *
服务端启动pppd程序
pppd /dev/ttyS0 9600 auth local lock defaultroute debug nodetach 172.16.1.1:172.16.1.2 ms-dns 8.8.8.8 require-eap
客户端启动pppd程序
pppd noauth local lock defaultroute debug nodetach /dev/ttyS0 9600 user aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbb password notpassword
下图我们发现程序没有崩溃,服务端打印出的username只有255个字节,我们输入的username255+5个字节,可惜这不是服务端对接收的内容进行的验证,这是在客户端限制了用户输入的用户名长度
0x2 漏洞分析
pppd进行MD5-Challenge的时候,通过eap_chap_response把密码hash、用户名和用户名长度一起发送到了服务端,用户名保存在一个256大小的栈变量中。
所以在我们上边的测试中,其实只发送了255长度的username到服务端。
6 typenum = id;
5 MD5_Update(&mdContext &typenum 1);
4 MD5_Update(&mdContext (u_char *)secret secret_len);
3 BZERO(secret sizeof (secret));
2 MD5_Update(&mdContext inp vallen);
1 MD5_Final(hash &mdContext);
1454 ** eap_chap_response(esp id hash esp->es_client.ea_name
1 esp->es_client.ea_namelen);
2 break;
787 eap_authwithpeer(unit user);
20 void
19 eap_authwithpeer(unit localname)
18 int unit;
17 char *localname;
16 {
15 eap_state *esp = &eap_states[unit];
14
13 /* Save the peer name we're given */
12 esp->es_client.ea_name = localname;
11 esp->es_client.ea_namelen = strlen(localname);
10
9 esp->es_client.ea_state = eapListen;
8
7 /*
6 * Start a timer so that if the other end just goes
5 * silent we don't sit here waiting forever.
4 */
3 if (esp->es_client.ea_timeout > 0)
2 TIMEOUT(eap_client_timeout (void *)esp
1 esp->es_client.ea_timeout);
257 }
extern char user[MAXNAMELEN];/* Our name for authenticating ourselves */
extern char passwd[MAXSECRETLEN]; /* Password for PAP or CHAP */
#define MAXNAMELEN 256 /* max length of hostname or name for auth */
服务端接收的时候,vallen是已经处理的长度,len是接收的总长度,sizeof (rhostname)是username最大的长度和客户端一样256的数组,所以if判断手误了?
最后MD5-Challenge没处理好长度验证。当发送大于256长度的字符串时,发生益处。
if (vallen >= len + sizeof (rhostname)) {
dbglog("EAP: trimming really long peer name down");
BCOPY(inp + vallen rhostname sizeof (rhostname) - 1;
rhostname[sizeof (rhostname) - 1] = '\0';
} else {
BCOPY(inp + vallen rhostname len - vallen);
rhostname[len - vallen] = '\0';
}
0x3 其他
想要触发服务端崩溃,可以像参考链接中的博主一样,直接修改eap_chap_response中的username送的内容。也可以像另一位博主一样,wireshark抓包,修改数据包中username内容,在写脚本发包到服务端,
0x4一半翻译一半补充的参考链接
https://gist.github.com/nstarke/551433bcc72ff95588e168a0bb666124