이번에도 리버싱 하나 풀었습니다.
REV
x0rr3al?!!
더보기
IDA로 디컴파일합니다.
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; // [rsp+Fh] [rbp-51h]
int i; // [rsp+10h] [rbp-50h]
int v6; // [rsp+14h] [rbp-4Ch]
char s[56]; // [rsp+20h] [rbp-40h] BYREF
unsigned __int64 v8; // [rsp+58h] [rbp-8h]
v8 = __readfsqword(0x28u);
if ( (unsigned int)sub_17A4(a1, a2, a3) )
{
sub_16AD();
exit(1);
}
if ( ptrace(PTRACE_TRACEME, 0LL, 1LL, 0LL) == -1 )
{
sub_16AD();
exit(1);
}
v6 = sub_1483(main, 512LL);
sub_1355();
sub_13F8();
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
{
sub_16AD();
exit(1);
}
printf("p4ss m3 th3 fl4g: ");
__isoc99_scanf("%53s", s);
if ( strlen(s) == 53 )
{
for ( i = 0; i <= 52; ++i )
{
v4 = sub_150A(s[i], 0);
if ( (unsigned int)sub_14F7(v4) != dword_40A0[i] )
goto LABEL_8;
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
{
sub_16AD();
exit(1);
}
}
sub_1614();
return 0LL;
}
else
{
LABEL_8:
sub_156A();
return 0LL;
}
}
main 함수에서 중요하게 봐야 할 코드는 다음과 같습니다.
...
if ( (unsigned int)sub_17A4(a1, a2, a3) )
{
sub_16AD();
exit(1);
}
if ( ptrace(PTRACE_TRACEME, 0LL, 1LL, 0LL) == -1 )
{
sub_16AD();
exit(1);
}
...
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
{
sub_16AD();
exit(1);
}
...
printf("p4ss m3 th3 fl4g: ");
__isoc99_scanf("%53s", s);
if ( strlen(s) == 53 )
{
for ( i = 0; i <= 52; ++i )
{
v4 = sub_150A(s[i], 0);
if ( (unsigned int)sub_14F7(v4) != dword_40A0[i] )
goto LABEL_8;
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
{
sub_16AD();
exit(1);
}
...
처음에 나오는 3개의 if문은 동적 디버깅으로 가볍게 우회해 줍니다.
(심볼이 없기 때문에 ida로 오프셋 참고해서 브포 걸고 ni 갈기면서 한 줄씩 봐야함..)
gdb로 디버깅 해보니 3개의 if 문은 각각 다음과 같은 곳에 있었습니다.
(set 명령으로 우회 가능)
pwndbg> x/2i 0x00005555555559c9
0x5555555559c9: test eax,eax
0x5555555559cb: je 0x5555555559e1
pwndbg> x/2i 0x00005555555559ff
0x5555555559ff: cmp rax,0xffffffffffffffff
0x555555555a03: jne 0x555555555a19
pwndbg> x/2i 0x0000555555555a5d
0x555555555a5d: cmp DWORD PTR [rbp-0x4c],eax
0x555555555a60: je 0x555555555a76
다음으로 플래그를 입력하고 검증하는 구문입니다.
printf("p4ss m3 th3 fl4g: ");
__isoc99_scanf("%53s", s);
if ( strlen(s) == 53 )
{
for ( i = 0; i <= 52; ++i )
{
v4 = sub_150A(s[i], 0);
if ( (unsigned int)sub_14F7(v4) != dword_40A0[i] )
goto LABEL_8;
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
{
sub_16AD();
exit(1);
}
먼저 입력 값의 길이는 53이 되어야 합니다.
다음으로 다음과 같은 식을 만족해야합니다.
for ( i = 0; i <= 52; ++i )
{
v4 = sub_150A(s[i], 0);
if ( (unsigned int)sub_14F7(v4) != dword_40A0[i] )
if ( v6 != (unsigned int)sub_1483(main, 512LL) )
}
역연산을 위해 거꾸로 살펴보겠습니다.
v6을 검사하는 구문은 위의 3개의 if문 중 마지막 세 번째 if문을 우회할 때 설정한 값으로 인해 자동 통과입니다.
sub_14F7 함수는 다음과 같습니다.
__int64 __fastcall sub_14F7(int a1)
{
return a1 ^ 0x12u;
}
비교하는 값의 배열인 dword_40A0은 다음과 같습니다.
pwndbg> x/55wx 0x5555555580a0
0x5555555580a0: 0x0000007e 0x0000007b 0x0000006b 0x0000007c
0x5555555580b0: 0x0000006e 0x00000073 0x0000007f 0x0000003b
0x5555555580c0: 0x0000003c 0x00000063 0x00000057 0x0000003c
0x5555555580d0: 0x00000066 0x0000007c 0x00000039 0x00000057
0x5555555580e0: 0x0000006c 0x0000003b 0x0000006a 0x0000007d
0x5555555580f0: 0x0000006f 0x0000006f 0x0000003b 0x0000007a
0x555555558100: 0x0000007b 0x00000057 0x0000003c 0x0000007a
0x555555558110: 0x0000003b 0x00000057 0x00000066 0x00000038
0x555555558120: 0x00000057 0x00000065 0x0000003c 0x0000007c
0x555555558130: 0x0000006b 0x00000060 0x00000057 0x0000006e
0x555555558140: 0x00000038 0x0000007a 0x00000057 0x0000007c
0x555555558150: 0x00000060 0x0000003b 0x00000057 0x0000003b
0x555555558160: 0x00000039 0x0000003b 0x0000003b 0x0000003f
0x555555558170: 0x00000075 0x00000000 0x00000000
다음으로 sub_150A 함수는 다음과 같습니다.
__int64 __fastcall sub_150A(unsigned __int8 a1, int a2)
{
if ( a2 <= 3 )
return (unsigned __int8)sub_150A(dest[11 * a2] ^ a1, a2 + 1);
else
return a1;
}
재귀 함수인 것을 확인했습니다.
dest 배열은 char 타입으로 다음과 같습니다.
.bss:0000000000004180 ; char dest[10]
.bss:0000000000004180 dest db 0Ah dup(?) ; DATA XREF: sub_1355+F↑o
.bss:0000000000004180 ; sub_1355+22↑o ...
.bss:000000000000418A ; char byte_418A[10]
.bss:000000000000418A byte_418A db 0Ah dup(?) ; DATA XREF: sub_1355+35↑o
.bss:000000000000418A ; sub_1355+48↑o
.bss:0000000000004194 ; char byte_4194[10]
.bss:0000000000004194 byte_4194 db 0Ah dup(?) ; DATA XREF: sub_1355+5B↑o
.bss:0000000000004194 ; sub_1355+6E↑o
.bss:000000000000419E ; char byte_419E[10]
.bss:000000000000419E byte_419E db 0Ah dup(?) ; DATA XREF: sub_1355+81↑o
.bss:000000000000419E ; sub_1355+94↑o
10개만 넣으면 out of index가 발생할테니 넉넉하게 60개 정도 찾아줍니다.
pwndbg> x/60cx 0x555555558180
0x555555558180: 0x73 0x33 0x63 0x52 0x33 0x74 0x73 0x33
0x555555558188: 0x00 0x00 0x76 0x73 0x63 0x74 0x66 0x76
0x555555558190: 0x73 0x63 0x74 0x69 0x69 0x61 0x6d 0x66
0x555555558198: 0x72 0x6e 0x6f 0x77 0x30 0x6b 0x6b 0x65
0x5555555581a0: 0x79 0x77 0x30 0x77 0x6b 0x65 0x79 0x77
0x5555555581a8: 0x77 0x6b 0x65 0x79 0x77 0x00 0x65 0x79
0x5555555581b0: 0x77 0x30 0x77 0x6b 0x65 0x79 0x77 0x00
0x5555555581b8: 0x00 0x00 0x00 0x00
역연산을 위해 거꾸로 살펴봤지만 모든 연산이 xor 연산이기 때문에
순서에 상관없이 동일한 연산을 수행하면 플래그를 복원할 수 있습니다.
dest = [0x73,0x33,0x63,0x52,0x33,0x74,0x73,0x33
,0x00,0x00,0x76,0x73,0x63,0x74,0x66,0x76
,0x73,0x63,0x74,0x69,0x69,0x61,0x6d,0x66
,0x72,0x6e,0x6f,0x77,0x30,0x6b,0x6b,0x65
,0x79,0x77,0x30,0x77,0x6b,0x65,0x79,0x77
,0x77,0x6b,0x65,0x79,0x77,0x00,0x65,0x79
,0x77,0x30,0x77,0x6b,0x65,0x79,0x77,0x00
,0x00,0x00,0x00,0x00]
def sub_150A(a1,a2):
if a2 <= 3:
return sub_150A(dest[11 * a2] ^ a1, a2 + 1)
else:
return a1
v2=[0x0000007e,0x0000007b,0x0000006b,0x0000007c
,0x0000006e,0x00000073,0x0000007f,0x0000003b
,0x0000003c,0x00000063,0x00000057,0x0000003c
,0x00000066,0x0000007c,0x00000039,0x00000057
,0x0000006c,0x0000003b,0x0000006a,0x0000007d
,0x0000006f,0x0000006f,0x0000003b,0x0000007a
,0x0000007b,0x00000057,0x0000003c,0x0000007a
,0x0000003b,0x00000057,0x00000066,0x00000038
,0x00000057,0x00000065,0x0000003c,0x0000007c
,0x0000006b,0x00000060,0x00000057,0x0000006e
,0x00000038,0x0000007a,0x00000057,0x0000007c
,0x00000060,0x0000003b,0x00000057,0x0000003b
,0x00000039,0x0000003b,0x0000003b,0x0000003f
,0x00000075]
for i in range(len(v2)):
v2[i] = v2[i] ^ 0x12
v2[i] = sub_150A(v2[i],0)
print(chr(v2[i]),end='')
vsctf{w34k_4nt1_d3bugg3rs_4r3_n0_m4tch_f0r_th3_31337}
'CTF' 카테고리의 다른 글
osu!gaming CTF 2024 (0) | 2024.03.04 |
---|---|
TetCTF 2024 (0) | 2024.01.29 |
CSAW CTF Qualification Round 2023 (0) | 2023.09.18 |
SECCON CTF 2023 Quals (0) | 2023.09.17 |
BDSec CTF 2023 (0) | 2023.07.22 |