因此,我正在处理一个难题,在C程序二进制文件中发现一个漏洞,该漏洞允许程序执行命令(在Linux中使用有效的UID(。
我真的很难找到如何用这个特殊的程序做到这一点。
有关功能(主要功能(的拆卸:
**************************************************************
* *
* 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 verifyn",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;
}
似乎是(总结(在只读模式下使用open打开作为第一个参数传递的文件。如果成功,它将分叉并使用子进程执行整洁,以验证文件是有效的XHTML。
对我来说,这并不是一个明显的弱点,我可以在这里利用它。我已经研究过整洁命令的漏洞,但没有找到任何有用的东西。
任何帮助都将不胜感激!
在常规C代码中,execlp("tidy","tidy","-asxml",0);
是不正确的,因为execlp()
需要一个空指针参数来标记参数列表的末尾。
当在指针上下文中使用时,0
是一个空指针,但事实并非如此。然而,在指针大小和传递约定与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
的可执行程序,并以命令行参数-asxml
的tidy
运行此程序。由于它更改了有效的uid和gid,如果程序是setuidroot,这是一个问题,因为您可以在系统目录之前的PATH
变量中出现的目录中创建一个名为tidy
的程序,并且该程序将使用修改后的权限运行。
另一个潜在的问题是程序没有检查系统调用setreuid()
和setregid()
的故障。尽管这些调用对于传递的参数不太可能失败,如手册页面中所述,但省略对setreuid()
的失败返回的检查是一个严重的安全错误在失败的情况下,真实有效的uid(或gid(不会更改,进程可能会以root权限分叉和执行。