本文为看雪论坛精华文章
看雪论坛作者ID:zackery
wget https://www.sudo.ws/dist/sudo-1.8.21.tar.gz
tar xzf sudo-1.8.21.tar.gz
$ docker pull aflplusplus/aflplusplus
# 拉取afl++
$ docker run -ti -v /location/of/your/target:/src
[-v /location/of/your/afl_src/:AFLplusplus ]
aflplusplus/aflplusplus /bin/bash
# 启动docker afl
# 映射代码文件夹,如果本地没有afl++的源代码的话,建议也映射一份,便于后续操作
./configure --prefix=/src/origin_compile
make
make install
cd /src/origin_compile/bin
'' aaaaaaaaaaaaaaa ./sudoedit -s
这个补丁,可以通过在源代码文件夹中搜索getuid,将getuid()和getgid()修改为1000即可。
--- ./src/sudo.c
+++ ./src/sudo.c
}
ud->sid = getsid(0);
- ud->uid = getuid();
+ ud->uid = 1000;
ud->euid = geteuid();
- ud->gid = getgid();
+ ud->gid = 1000;
ud->egid = getegid();
为了实现对argv的fuzzing,我们可以将/aflplusplus/utils/argv_fuzzing/argv-fuzz-inl.h复制到sudo的源代码目录../sudo/src下,并对sudo.c进行相应的补丁。
+++ sudo.c 2023-01-22 01:09:38.175635142 -0800
--- sudo.c_org1 2023-01-22 01:06:27.035319663 -0800
@@ -14,7 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+
@@ -134,7 +133,6 @@
int
main(int argc, char *argv[], char *envp[])
{
+ AFL_INIT_ARGV();
int nargc, ok, status = 0;
char **nargv, **env_add;
char **user_info, **command_info, **argv_out, **user_env_out;
do {
argv = afl_init_argv(&argc);
} while (0)
······
static char **afl_init_argv(int *argc) {
static char in_buf[MAX_CMDLINE_LEN];
static char *ret[MAX_CMDLINE_PAR];
char *ptr = in_buf;
int rc = 0;
if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {}
while (*ptr && rc < MAX_CMDLINE_PAR) {
ret[rc] = ptr;
if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++;
rc++;
while (*ptr)
ptr++;
ptr++;
}
*argc = rc;
return ret;
,
}
我们要明确测试的目的,普通用户以特定的参数打开sudo,导致程序崩溃,因此权限认证不应该被通过,我们试着取消执行sudo时权限认证环节。
首先,我们需要找到权限认证的代码片段,这里可以通过gdb调试,查看sudo运行到输入密码时程序的状态。
make clean
disable-shared ./configure --prefix=~/sudo_gdb_test_auth --
make
make install
sudo chown root:root ./sudo
sudo chmod u+s ./sudo
ls -l
./sudo ls
Password:(expecting input)
ps -ef |grep sudo
root 206957 206889 0 19:15 pts/4 00:00:00 ./sudo ls
pwndbg> bt
#0 0x00007f1421f7dfd2 in __GI___libc_read (fd=fd@entry=5, buf=buf@entry=0x7ffd483eb217, nbytes=nbytes@entry=1) at ../sysdeps/unix/sysv/linux/read.c:26
#1 0x00005622b6cac7af in read (__nbytes=1, __buf=0x7ffd483eb217, __fd=5) at /usr/include/x86_64-linux-gnu/bits/unistd.h:44
#2 getln (fd=fd@entry=5, buf=buf@entry=0x5622b6d0b480 <buf> "", feedback=feedback@entry=0, bufsiz=256) at ./tgetpass.c:311
#3 0x00005622b6cacbd0 in tgetpass (prompt=0x5622b6e87bb0 "Password: ", timeout=300, flags=0, callback=callback@entry=0x7ffd483ebd50) at ./tgetpass.c:178
#4 0x00005622b6c9c81e in sudo_conversation (num_msgs=<optimized out>, msgs=<optimized out>, replies=0x7ffd483eb918, callback=0x7ffd483ebd50) at ./conversation.c:70
#5 0x00005622b6cd8485 in auth_getpass (prompt=0x5622b6e87bb0 "Password: ", timeout=<optimized out>, type=type@entry=1, callback=callback@entry=0x7ffd483ebd50) at ./auth/sudo_auth.c:426
#6 0x00005622b6cd88d6 in verify_user (pw=0x5622b6e7e158, prompt=<optimized out>, prompt@entry=0x5622b6e87bb0 "Password: ", validated=validated@entry=2, callback=callback@entry=0x7ffd483ebd50) at ./auth/sudo_auth.c:282
#7 0x00005622b6cd93f1 in check_user_interactive (auth_pw=0x5622b6e7e158, mode=<optimized out>, validated=2) at ./check.c:149
#8 check_user (validated=validated@entry=2, mode=<optimized out>) at ./check.c:212
#9 0x00005622b6cc2af2 in sudoers_policy_main (argc=argc@entry=1, argv=argv@entry=0x7ffd483ec1a0, pwflag=pwflag@entry=0, env_add=env_add@entry=0x0, closure=closure@entry=0x7ffd483ebeb0) at ./sudoers.c:423
#10 0x00005622b6cbcfc4 in sudoers_policy_check (argc=1, argv=0x7ffd483ec1a0, env_add=0x0, command_infop=0x7ffd483ebf28, argv_out=0x7ffd483ebf30, user_env_out=0x7ffd483ebf38) at ./policy.c:775
#11 0x00005622b6c9ae57 in policy_check (plugin=0x5622b6d0c000 <policy_plugin>, user_env_out=0x7ffd483ebf38, argv_out=0x7ffd483ebf30, command_info=0x7ffd483ebf28, env_add=0x0, argv=0x7ffd483ec1a0, argc=1) at ./sudo.c:1149
#12 main (argc=argc@entry=2, argv=argv@entry=0x7ffd483ec198, envp=0x7ffd483ec1b0) at ./sudo.c:247
#13 0x00007f1421e94083 in __libc_start_main (main=0x5622b6c9aa50 <main>, argc=2, argv=0x7ffd483ec198, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffd483ec188) at ../csu/libc-start.c:308
#14 0x00005622b6c9c6be in _start () at ./sudo.c:798
-rwsr-xr-x 1 root root 1647712 Jan 23 06:50 sudo
lrwxrwxrwx 1 root root 4 Jan 23 06:50 sudoedit -> sudo
-rwxr-xr-x 1 root root 318384 Jan 23 06:50 sudoreplay
$ echo -ne "sudoeditid" | ./sudo
usage: sudo -h | -K | -k | -V
usage: sudo -v [-AknS] [-g group] [-h host] [-p prompt] [-u user]
usage: sudo -l [-AknS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command]
usage: sudo [-AbEHknPS] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]
usage: sudo -e [-AknS] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] file ...
$ echo "sudoid" | ./sudoedit
usage: sudoedit [-AknS] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] file ...
os_init_common,os_init_common会调用initprogname初始化程序名。
int
os_init_common(int argc, char *argv[], char *envp[])
{
initprogname(argc > 0 ? argv[0] : "sudo");
preload_static_symbols();
gc_init();
return 0;
}
void
initprogname(const char *name)
{
extern const char *__progname;
if (__progname != NULL && *__progname != '')
progname = __progname;
else
# endif
if ((progname = strrchr(name, '/')) != NULL) {
progname++;
} else {
progname = name;
}
/* Check for libtool prefix and strip it if present. */
if (progname[0] == 'l' && progname[1] == 't' && progname[2] == '-' &&
progname[3] != '')
progname += 3;
}
--- ../origin_file/progname.c 2023-01-27 01:21:37.829958000 -0800
+++ progname.c 2023-01-27 01:23:42.824771537 -0800
void
initprogname(const char *name)
{
-# ifdef HAVE___PROGNAME
- extern const char *__progname;
-
- if (__progname != NULL && *__progname != '')
- progname = __progname;
- else
-# endif
if ((progname = strrchr(name, '/')) != NULL) {
progname++;
} else {
重新开一个容器,将最终代码放入/src,创建/fuzz,同时设置好主机跟踪目录,便于观察fuzz情况及其进展。
sudo docker run -it
-v path/to/all_change:/src
-v path/to/trace_paper_fuzz:/fuzz
/bin/bash
useradd -u 1000 aflfuzzer
cd /src
make clean
"-g" LDFLAGS="-g" CC=afl-clang-fast ./configure --prefix=/fuzz/release --disable-shared CFLAGS=
make
make install
echo -ne "sudoeditx00-sx00x5cx00aaaaaaaaaaaaaaaaaaaaaaaaaax00x00" | ./sudo
# 为啥不用直接用,我觉得可以,但是我本机解析经常出问题,因此本文全篇都是用xHH
cd /fuzz
mkdir {input,output}
echo -ne "sudox00idx00x00" > input/payload1
echo -ne "sudoeditx00idx00x00" > input/payload2
afl-fuzz -i input/ -o output/ -D -M Master /fuzz/release/bin/sudo
可以创建多个从fuzzer辅助测试
afl-fuzz -i input/ -o output/ -D -S slave1 /fuzz/release/bin/sudo
使用afl-gcc编译:
make clean
"-g" LDFLAGS="-g" CC=afl-gcc ./configure --prefix=/src/gcc_compile --disable-shared CFLAGS=
make
make install
./sudoedit
__afl_setup首先检查__afl_setup_failure是否为空,如果不为空代表已经初始化失败过,调用__afl_return返回,否则调用__afl_setup_first进行初始化。
__afl_setup_first会保存所有寄存器的值,然后调用getenv获取SHM_ENV_VAR(fuzz程序保存的共享内存id)
为什么getenv会被插桩呢,getenv原本是c库函数,但是在sudo源代码中的env_hook.c中定义同名的getenv函数。
__dso_public char *
getenv(const char *name)
{
char *val = NULL;
switch (process_hooks_getenv(name, &val)) {
case SUDO_HOOK_RET_STOP:
return val;
case SUDO_HOOK_RET_ERROR:
return NULL;
default:
return getenv_unhooked(name);
}
}
afl-as.h
"n"
"/* --- AFL TRAMPOLINE (64-BIT) --- */n"
"n"
".align 4n"
"n"
"leaq -(128+24)(%%rsp), %%rspn"
"movq %%rdx, 0(%%rsp)n"
"movq %%rcx, 8(%%rsp)n"
"movq %%rax, 16(%%rsp)n"
"movq $0x%08x, %%rcxn"
"call __afl_maybe_logn"
"movq 16(%%rsp), %%raxn"
"movq 8(%%rsp), %%rcxn"
"movq 0(%%rsp), %%rdxn"
"leaq (128+24)(%%rsp), %%rspn"
"n"
"/* --- END --- */n"
"n";
看雪ID:zackery
https://bbs.kanxue.com/user-home-970576.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):通过AFL++复现sudo漏洞的一次尝试
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论