2020. 8. 19. 01:32, CTF/CTF_writeup
T express에서 워낙 삽질을 많이 해서 대회 25분 남긴채 문제를 열어봤었다.
25분만에 풀뻔 했지만 아쉽게도 시간 내에는 풀지 못한 문제 ㅠㅠ
대회 후에 밥먹고 풀어보니 바로 풀렸던 문제였다.
IDA 분석 & 취약점
void __noreturn pwnme()
{
int buf[4]; // [esp+4h] [ebp-34h]
int (*v1)(); // [esp+14h] [ebp-24h]
int (*v2)(); // [esp+18h] [ebp-20h]
int (*v3)(); // [esp+1Ch] [ebp-1Ch]
void (__noreturn *v4)(); // [esp+20h] [ebp-18h]
int v5; // [esp+24h] [ebp-14h]
unsigned int v6; // [esp+2Ch] [ebp-Ch]
v6 = __readgsdword(0x14u);
memset(buf, 0, 0x28u);
v1 = func1;
v2 = func2;
v3 = func3;
v4 = func4;
while ( 1 )
{
puts("\n0. Show flag");
puts("1. Show environment variables");
puts("2. Show directory contents");
puts("3. Exit");
printf("Select > ");
fflush(stdout);
read(0, buf, 0x10u);
v5 = atoi((const char *)buf);
if ( v5 <= 3 )
((void (*)(void))buf[v5 + 4])();
else
printf("Invalid input: %s\n", buf);
}
}
우선, 문제 이름처럼 pie가 걸려 있었는데, 입력을 0x10만큼 보내주면 뒤에 있는 부분까지 다 출력되서 쉽게 pie_leak이 가능하다.
그리고 atoi 에서 oob가 나서 음수값 참조가 가능하다. 즉, 내가 원하는 곳으로 jump가 가능하다는 의미이다.
0x00000968 <+190>: add esp,0x10
0x0000096b <+193>: sub esp,0x4
0x0000096e <+196>: push 0x10
0x00000970 <+198>: lea eax,[ebp-0x34]
0x00000973 <+201>: push eax
0x00000974 <+202>: push 0x0
0x00000976 <+204>: call 0x540 <read@plt>
0x0000097b <+209>: add esp,0x10
0x0000097e <+212>: sub esp,0xc
0x00000981 <+215>: lea eax,[ebp-0x34]
0x00000984 <+218>: push eax
0x00000985 <+219>: call 0x5f0 <atoi@plt>
0x0000098a <+224>: add esp,0x10
0x0000098d <+227>: mov DWORD PTR [ebp-0x14],eax
0x00000990 <+230>: mov eax,DWORD PTR [ebp-0x14]
0x00000993 <+233>: cmp eax,0x3
0x00000996 <+236>: jle 0x9b0 <pwnme+262>
0x00000998 <+238>: sub esp,0x8
0x0000099b <+241>: lea eax,[ebp-0x34]
0x0000099e <+244>: push eax
0x0000099f <+245>: lea eax,[ebx-0x13d5]
0x000009a5 <+251>: push eax
0x000009a6 <+252>: call 0x550 <printf@plt>
0x000009ab <+257>: add esp,0x10
0x000009ae <+260>: jmp 0x9bc <pwnme+274>
0x000009b0 <+262>: mov eax,DWORD PTR [ebp-0x14]
0x000009b3 <+265>: add eax,0x4
0x000009b6 <+268>: mov eax,DWORD PTR [ebp+eax*4-0x34]
0x000009ba <+272>: call eax
이 부분은 pwnme 함수에서 read 와 call eax 사이 부분인데,
call eax에서 다시 read 하는 부분으로 뛰게 되면, esp가 커지게 된다. 하지만 ebp는 그대로이기 때문에, 몇번 반복하다 보면
read함수 내부에서 read로 입력하는 부분이 esp보다 커지게 되고, 즉 read함수의 ret_addr을 덮을 수 있게 된다.
확인해보니, 2번 read로 뛰고, 3번째에 system + "/bin/sh" 를 넣게 되면 정확히 read의 ret_addr을 덮게 된다.
여기서 system은 system_plt가 아닌, func2의 함수 내부에 있는 call system을 사용했다.
# 알게된 점
저렇게 내부에서 반복하면서 read받는 위치가 esp보다 커지면서 read함수의 ret을 덮어본 것은 처음이었다. (새로 알게됨)
다른 풀이를 보니 push 0x10 다음으로 가서 read 입력 값을 0x10보다 더 많이 받음으로써 rop로 푸는 방법도 있었다.
'CTF > CTF_writeup' 카테고리의 다른 글
[SECCON CTF 2021] oOoOoO (0) | 2022.01.03 |
---|---|
[SECCON CTF 2021] pppp (0) | 2021.12.28 |
[SCTF 2020] T express (0) | 2020.08.19 |
[Hack.lu 2018] Baby_Kernel_1 (1) | 2020.04.04 |
[zer0pts] dirty laundry (0) | 2020.03.12 |
Comments, Trackbacks