这个C程序是否存在命令执行漏洞?

Is there a command execution vulnerability in this C program?

所以我正在研究一个挑战问题,以找到 C 程序二进制文件中的漏洞,该漏洞允许程序执行命令(使用 Linux 中的有效 UID)。

我真的很难找到如何使用这个特定程序来做到这一点。

问题函数(main函数)的反汇编:


 **************************************************************
 *                                                            *
 *  FUNCTION                                                  *
 **************************************************************
 int __cdecl main(int argc, char * * argv)
     int               EAX:4          <RETURN>
     int               Stack[0x4]:4   argc
     char * *          Stack[0x8]:4   argv                            XREF[2]:     000109b0(R), 
                                                                                   000109dd(R)  
     undefined4        Stack[-0x8]:4  local_8                         XREF[1]:     00010bcb(R)  
     int               Stack[-0xc]:4  in                              XREF[5]:     000109f0(W), 
                                                                                   000109f3(R), 
                                                                                   00010ad4(R), 
                                                                                   00010b27(R), 
                                                                                   00010b59(R)  
     int               Stack[-0x10]:4 fd                              XREF[6]:     00010a1f(W), 
                                                                                   00010a22(R), 
                                                                                   00010aa5(R), 
                                                                                   00010ab2(R), 
                                                                                   00010ac9(R), 
                                                                                   00010b4e(R)  
     pid_t             Stack[-0x14]:4 pid                             XREF[4]:     00010a6b(W), 
                                                                                   00010a6e(R), 
                                                                                   00010a8b(R), 
                                                                                   00010b6a(R)  
     int[2]            Stack[-0x1c]:8 pipefd                          XREF[3,3]:   00010a3f(*), 
                                                                                   00010a95(R), 
                                                                                   00010b42(R), 
                                                                                   00010abd(R), 
                                                                                   00010b0f(R), 
                                                                                   00010b36(R)  
     char              Stack[-0x1d]:1 c                               XREF[2]:     00010b14(*), 
                                                                                   00010b23(*)  
     int               Stack[-0x24]:4 status                          XREF[2]:     00010b66(*), 
                                                                                   00010b75(R)  
                     main                                    XREF[5]:     Entry Point(*), 
                                                                          _start:00010866(*), 00010d30, 
                                                                                  00010da0(*), 00011f34(*)  
0001097d 55              PUSH       EBP
0001097e 89 e5           MOV        EBP,ESP
00010980 53              PUSH       EBX
00010981 83 ec 1c        SUB        ESP,0x1c
00010984 e8 87 16        CALL       <EXTERNAL>::geteuid                              __uid_t geteuid(void)
         00 00
00010989 89 c3           MOV        EBX,EAX
0001098b e8 80 16        CALL       <EXTERNAL>::geteuid                              __uid_t geteuid(void)
         00 00
00010990 53              PUSH       EBX
00010991 50              PUSH       EAX
00010992 e8 9d 16        CALL       <EXTERNAL>::setreuid                             int setreuid(__uid_t __ruid, __u
         00 00
00010997 83 c4 08        ADD        ESP,0x8
0001099a e8 75 16        CALL       <EXTERNAL>::getegid                              __gid_t getegid(void)
         00 00
0001099f 89 c3           MOV        EBX,EAX
000109a1 e8 6e 16        CALL       <EXTERNAL>::getegid                              __gid_t getegid(void)
         00 00
000109a6 53              PUSH       EBX
000109a7 50              PUSH       EAX
000109a8 e8 9b 16        CALL       <EXTERNAL>::setregid                             int setregid(__gid_t __rgid, __g
         00 00
000109ad 83 c4 08        ADD        ESP,0x8
000109b0 8b 45 0c        MOV        EAX,dword ptr [EBP + argv]
000109b3 83 c0 04        ADD        EAX,0x4
000109b6 8b 00           MOV        EAX,dword ptr [EAX]
000109b8 85 c0           TEST       EAX,EAX
000109ba 75 21           JNZ        LAB_000109dd
000109bc a1 98 1f        MOV        EAX,[stderr]
         01 00
000109c1 50              PUSH       EAX
000109c2 6a 22           PUSH       0x22
000109c4 6a 01           PUSH       0x1
000109c6 68 50 0c        PUSH       s_Please_specify_the_file_to_verif_00010c50      = "Please specify the file to ve
         01 00
000109cb e8 50 16        CALL       <EXTERNAL>::fwrite                               size_t fwrite(void * __ptr, size
         00 00
000109d0 83 c4 10        ADD        ESP,0x10
000109d3 b8 01 00        MOV        EAX,0x1
         00 00
000109d8 e9 ee 01        JMP        LAB_00010bcb
         00 00
                     LAB_000109dd                                    XREF[1]:     000109ba(j)  
000109dd 8b 45 0c        MOV        EAX,dword ptr [EBP + argv]
000109e0 83 c0 04        ADD        EAX,0x4
000109e3 8b 00           MOV        EAX,dword ptr [EAX]
000109e5 6a 00           PUSH       0x0
000109e7 50              PUSH       EAX
000109e8 e8 43 16        CALL       <EXTERNAL>::open                                 int open(char * __file, int __of
         00 00
000109ed 83 c4 08        ADD        ESP,0x8
000109f0 89 45 f8        MOV        dword ptr [EBP + in],EAX
000109f3 83 7d f8 00     CMP        dword ptr [EBP + in],0x0
000109f7 79 17           JNS        LAB_00010a10
000109f9 68 73 0c        PUSH       DAT_00010c73                                     = 6Fh    o
         01 00
000109fe e8 19 16        CALL       <EXTERNAL>::perror                               void perror(char * __s)
         00 00
00010a03 83 c4 04        ADD        ESP,0x4
00010a06 b8 02 00        MOV        EAX,0x2
         00 00
00010a0b e9 bb 01        JMP        LAB_00010bcb
         00 00
                     LAB_00010a10                                    XREF[1]:     000109f7(j)  
00010a10 6a 02           PUSH       0x2
00010a12 68 78 0c        PUSH       s_/dev/null_00010c78                             = "/dev/null"
         01 00
00010a17 e8 14 16        CALL       <EXTERNAL>::open                                 int open(char * __file, int __of
         00 00
00010a1c 83 c4 08        ADD        ESP,0x8
00010a1f 89 45 f4        MOV        dword ptr [EBP + fd],EAX
00010a22 83 7d f4 00     CMP        dword ptr [EBP + fd],0x0
00010a26 79 17           JNS        LAB_00010a3f
00010a28 68 73 0c        PUSH       DAT_00010c73                                     = 6Fh    o
         01 00
00010a2d e8 ea 15        CALL       <EXTERNAL>::perror                               void perror(char * __s)
         00 00
00010a32 83 c4 04        ADD        ESP,0x4
00010a35 b8 05 00        MOV        EAX,0x5
         00 00
00010a3a e9 8c 01        JMP        LAB_00010bcb
         00 00
                     LAB_00010a3f                                    XREF[1]:     00010a26(j)  
00010a3f 8d 45 e8        LEA        EAX=>pipefd,[EBP + -0x18]
00010a42 50              PUSH       EAX
00010a43 e8 f8 15        CALL       <EXTERNAL>::pipe                                 int pipe(int * __pipedes)
         00 00
00010a48 83 c4 04        ADD        ESP,0x4
00010a4b 85 c0           TEST       EAX,EAX
00010a4d 79 17           JNS        LAB_00010a66
00010a4f 68 82 0c        PUSH       DAT_00010c82                                     = 70h    p
         01 00
00010a54 e8 c3 15        CALL       <EXTERNAL>::perror                               void perror(char * __s)
         00 00
00010a59 83 c4 04        ADD        ESP,0x4
00010a5c b8 03 00        MOV        EAX,0x3
         00 00
00010a61 e9 65 01        JMP        LAB_00010bcb
         00 00
                     LAB_00010a66                                    XREF[1]:     00010a4d(j)  
00010a66 e8 d9 15        CALL       <EXTERNAL>::fork                                 __pid_t fork(void)
         00 00
00010a6b 89 45 f0        MOV        dword ptr [EBP + pid],EAX
00010a6e 83 7d f0 00     CMP        dword ptr [EBP + pid],0x0
00010a72 79 17           JNS        LAB_00010a8b
00010a74 68 87 0c        PUSH       DAT_00010c87                                     = 66h    f
         01 00
00010a79 e8 9e 15        CALL       <EXTERNAL>::perror                               void perror(char * __s)
         00 00
00010a7e 83 c4 04        ADD        ESP,0x4
00010a81 b8 04 00        MOV        EAX,0x4
         00 00
00010a86 e9 40 01        JMP        LAB_00010bcb
         00 00
                     LAB_00010a8b                                    XREF[1]:     00010a72(j)  
00010a8b 83 7d f0 00     CMP        dword ptr [EBP + pid],0x0
00010a8f 0f 85 8c        JNZ        LAB_00010b21
         00 00 00
00010a95 8b 45 e8        MOV        EAX,dword ptr [EBP + pipefd[0]]
00010a98 6a 00           PUSH       0x0
00010a9a 50              PUSH       EAX
00010a9b e8 60 15        CALL       <EXTERNAL>::dup2                                 int dup2(int __fd, int __fd2)
         00 00
00010aa0 83 c4 08        ADD        ESP,0x8
00010aa3 6a 01           PUSH       0x1
00010aa5 ff 75 f4        PUSH       dword ptr [EBP + fd]
00010aa8 e8 53 15        CALL       <EXTERNAL>::dup2                                 int dup2(int __fd, int __fd2)
         00 00
00010aad 83 c4 08        ADD        ESP,0x8
00010ab0 6a 02           PUSH       0x2
00010ab2 ff 75 f4        PUSH       dword ptr [EBP + fd]
00010ab5 e8 46 15        CALL       <EXTERNAL>::dup2                                 int dup2(int __fd, int __fd2)
         00 00
00010aba 83 c4 08        ADD        ESP,0x8
00010abd 8b 45 ec        MOV        EAX,dword ptr [EBP + pipefd[1]]
00010ac0 50              PUSH       EAX
00010ac1 e8 8a 15        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010ac6 83 c4 04        ADD        ESP,0x4
00010ac9 ff 75 f4        PUSH       dword ptr [EBP + fd]
00010acc e8 7f 15        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010ad1 83 c4 04        ADD        ESP,0x4
00010ad4 ff 75 f8        PUSH       dword ptr [EBP + in]
00010ad7 e8 74 15        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010adc 83 c4 04        ADD        ESP,0x4
00010adf 6a 00           PUSH       0x0
00010ae1 68 8c 0c        PUSH       s_-asxml_00010c8c                                = "-asxml"
         01 00
00010ae6 68 93 0c        PUSH       DAT_00010c93                                     = 74h    t
         01 00
00010aeb 68 93 0c        PUSH       DAT_00010c93                                     = 74h    t
         01 00
00010af0 e8 17 15        CALL       <EXTERNAL>::execlp                               int execlp(char * __file, char *
         00 00
00010af5 83 c4 10        ADD        ESP,0x10
00010af8 68 98 0c        PUSH       s_execlp_00010c98                                = "execlp"
         01 00
00010afd e8 1a 15        CALL       <EXTERNAL>::perror                               void perror(char * __s)
         00 00
00010b02 83 c4 04        ADD        ESP,0x4
00010b05 b8 05 00        MOV        EAX,0x5
         00 00
00010b0a e9 bc 00        JMP        LAB_00010bcb
         00 00
                     LAB_00010b0f                                    XREF[1]:     00010b34(j)  
00010b0f 8b 45 ec        MOV        EAX,dword ptr [EBP + pipefd[1]]
00010b12 6a 01           PUSH       0x1
00010b14 8d 55 e7        LEA        EDX=>c,[EBP + -0x19]
00010b17 52              PUSH       EDX
00010b18 50              PUSH       EAX
00010b19 e8 1e 15        CALL       <EXTERNAL>::write                                ssize_t write(int __fd, void * _
         00 00
00010b1e 83 c4 0c        ADD        ESP,0xc
                     LAB_00010b21                                    XREF[1]:     00010a8f(j)  
00010b21 6a 01           PUSH       0x1
00010b23 8d 45 e7        LEA        EAX=>c,[EBP + -0x19]
00010b26 50              PUSH       EAX
00010b27 ff 75 f8        PUSH       dword ptr [EBP + in]
00010b2a e8 d5 14        CALL       <EXTERNAL>::read                                 ssize_t read(int __fd, void * __
         00 00
00010b2f 83 c4 0c        ADD        ESP,0xc
00010b32 85 c0           TEST       EAX,EAX
00010b34 75 d9           JNZ        LAB_00010b0f
00010b36 8b 45 ec        MOV        EAX,dword ptr [EBP + pipefd[1]]
00010b39 50              PUSH       EAX
00010b3a e8 11 15        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010b3f 83 c4 04        ADD        ESP,0x4
00010b42 8b 45 e8        MOV        EAX,dword ptr [EBP + pipefd[0]]
00010b45 50              PUSH       EAX
00010b46 e8 05 15        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010b4b 83 c4 04        ADD        ESP,0x4
00010b4e ff 75 f4        PUSH       dword ptr [EBP + fd]
00010b51 e8 fa 14        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010b56 83 c4 04        ADD        ESP,0x4
00010b59 ff 75 f8        PUSH       dword ptr [EBP + in]
00010b5c e8 ef 14        CALL       <EXTERNAL>::close                                int close(int __fd)
         00 00
00010b61 83 c4 04        ADD        ESP,0x4
00010b64 6a 00           PUSH       0x0
00010b66 8d 45 e0        LEA        EAX=>status,[EBP + -0x20]
00010b69 50              PUSH       EAX
00010b6a ff 75 f0        PUSH       dword ptr [EBP + pid]
00010b6d e8 b2 14        CALL       <EXTERNAL>::waitpid                              __pid_t waitpid(__pid_t __pid, i
         00 00
00010b72 83 c4 0c        ADD        ESP,0xc
00010b75 8b 45 e0        MOV        EAX,dword ptr [EBP + status]
00010b78 c1 f8 08        SAR        EAX,0x8
00010b7b 0f b6 c0        MOVZX      EAX,AL
00010b7e 83 f8 01        CMP        EAX,0x1
00010b81 74 18           JZ         LAB_00010b9b
00010b83 83 f8 02        CMP        EAX,0x2
00010b86 74 22           JZ         LAB_00010baa
00010b88 85 c0           TEST       EAX,EAX
00010b8a 75 2d           JNZ        LAB_00010bb9
00010b8c 68 9f 0c        PUSH       DAT_00010c9f                                     = 4Fh    O
         01 00
00010b91 e8 92 14        CALL       <EXTERNAL>::puts                                 int puts(char * __s)
         00 00
00010b96 83 c4 04        ADD        ESP,0x4
00010b99 eb 2b           JMP        LAB_00010bc6
                     LAB_00010b9b                                    XREF[1]:     00010b81(j)  
00010b9b 68 a4 0c        PUSH       s_Your_file_is_not_completely_comp_00010ca4      = "Your file is not completely c
         01 00
00010ba0 e8 83 14        CALL       <EXTERNAL>::puts                                 int puts(char * __s)
         00 00
00010ba5 83 c4 04        ADD        ESP,0x4
00010ba8 eb 1c           JMP        LAB_00010bc6
                     LAB_00010baa                                    XREF[1]:     00010b86(j)  
00010baa 68 ca 0c        PUSH       s_Your_file_contains_errors_00010cca             = "Your file contains errors"
         01 00
00010baf e8 74 14        CALL       <EXTERNAL>::puts                                 int puts(char * __s)
         00 00
00010bb4 83 c4 04        ADD        ESP,0x4
00010bb7 eb 0d           JMP        LAB_00010bc6
                     LAB_00010bb9                                    XREF[1]:     00010b8a(j)  
00010bb9 68 e4 0c        PUSH       s_I_can't_tell_if_your_file_is_XHT_00010ce4      = "I can't tell if your file is 
         01 00
00010bbe e8 65 14        CALL       <EXTERNAL>::puts                                 int puts(char * __s)
         00 00
00010bc3 83 c4 04        ADD        ESP,0x4
                     LAB_00010bc6                                    XREF[3]:     00010b99(j), 00010ba8(j), 
                                                                                  00010bb7(j)  
00010bc6 b8 00 00        MOV        EAX,0x0
         00 00
                     LAB_00010bcb                                    XREF[6]:     000109d8(j), 00010a0b(j), 
                                                                                  00010a3a(j), 00010a61(j), 
                                                                                  00010a86(j), 00010b0a(j)  
00010bcb 8b 5d fc        MOV        EBX,dword ptr [EBP + local_8]
00010bce c9              LEAVE
00010bcf c3              RET

根据 Ghidra 的说法,这反编译为:

int main(int argc,char **argv)

{
  __uid_t __euid;
  __uid_t __ruid;
  __gid_t __egid;
  __gid_t __rgid;
  int iVar1;
  int __fd;
  int iVar2;
  __pid_t __pid;
  ssize_t sVar3;
  uint uVar4;
  int status;
  char c;
  int pipefd [2];
  pid_t pid;
  int fd;
  int in;
  
  __euid = geteuid();
  __ruid = geteuid();
  setreuid(__ruid,__euid);
  __egid = getegid();
  __rgid = getegid();
  setregid(__rgid,__egid);
  if (argv[1] == (char *)0x0) {
    fwrite("Please specify the file to verify\n",1,0x22,stderr);
    iVar1 = 1;
  }
  else {
    iVar1 = open(argv[1],0);
    if (iVar1 < 0) {
      perror("open");
      iVar1 = 2;
    }
    else {
      __fd = open("/dev/null",2);
      if (__fd < 0) {
        perror("open");
        iVar1 = 5;
      }
      else {
        iVar2 = pipe(pipefd);
        if (iVar2 < 0) {
          perror("pipe");
          iVar1 = 3;
        }
        else {
          __pid = fork();
          if (__pid < 0) {
            perror("fork");
            iVar1 = 4;
          }
          else if (__pid == 0) {
            dup2(pipefd[0],0);
            dup2(__fd,1);
            dup2(__fd,2);
            close(pipefd[1]);
            close(__fd);
            close(iVar1);
            execlp("tidy","tidy","-asxml",0);
            perror("execlp");
            iVar1 = 5;
          }
          else {
            while( true ) {
              sVar3 = read(iVar1,&c,1);
              if (sVar3 == 0) break;
              write(pipefd[1],&c,1);
            }
            close(pipefd[1]);
            close(pipefd[0]);
            close(__fd);
            close(iVar1);
            waitpid(__pid,&status,0);
            uVar4 = status >> 8 & 0xff;
            if (uVar4 == 1) {
              puts("Your file is not completely compliant");
            }
            else if (uVar4 == 2) {
              puts("Your file contains errors");
            }
            else if (uVar4 == 0) {
              puts("OK!");
            }
            else {
              puts("I can\'t tell if your file is XHTML-compliant");
            }
            iVar1 = 0;
          }
        }
      }
    }
  }
  return iVar1;
}

它似乎(总而言之)打开以只读模式打开作为第一个参数传递的文件。如果成功,则分叉并使用子进程执行 tidy 以验证文件是有效的 XHTML。

对我来说,没有什么是我可以在这里使用的明显漏洞。我已经研究了 tidy 命令的漏洞,但并没有真正找到任何有用的东西。

如有任何帮助,我们将不胜感激!

在常规 C 代码中,execlp("tidy","tidy","-asxml",0); 是不正确的,因为 execlp() 需要一个空指针参数来标记参数列表的结尾。

0 在指针上下文中使用时是空指针,而 this 不是。然而,在指针具有与 int 相同大小和传递约定的体系结构上,例如 32 位 linux,传递 0 或传递 NULL 会生成相同的代码,因此草率不会受到惩罚。

在 64 位模式下,这样做是不正确的,但您可能会幸运地使用 x86_64 ABI,并且在这种情况下将传递 64 位 0 值。

在您自己的代码中,请避免此类陷阱并使用 NULL(char *)0 作为 execlp() 的最后一个参数。但是在这个列表中,Ghidra 生成的代码生成相同的汇编代码,并且在 32 位模式下,传递 0(char *)0 生成相同的代码,所以这里没有问题。

在您的上下文中,execlp("tidy","tidy","-asxml",0); 显示了另一个问题:它会在当前 PATH 和 运行 这个程序中寻找名称为 tidy 的可执行程序tidy 带有命令行参数 -asxml。因为它改变了有效的 uid 和 gid,如果程序是 setuid root,这是一个问题,因为你可以在系统目录和这个程序之前的 PATH 变量中出现的目录中创建一个名为 tidy 的程序将 运行 具有修改后的权限。

另一个潜在的问题是程序不检查系统调用setreuid()setregid() 是否失败。尽管这些调用不太可能因传递的参数而失败,如手册页中所述,setreuid() 忽略检查失败 return 是一个严重的安全错误。 在失败的情况下,真实有效的 uid(或 gid)不会改变,进程可能会 fork 并以 root 权限执行。