total_activ
SWING CTF v.01 풀이 본문



이전에 확인했던 코드들을 봤을때 scanf()라는 취약한 함수를 사용하고 있었고 void get_shell()로 cmd창을 불러오는 이름바 interactive()느낌의 코드함수도 존재했ㄷ. 즉, buf[0x28]에 해당하는 장소에 scanf를 통해 입력값을 넣은 뒤 오버플로우를 시켜서 return address 장소에 해당 get_shell()함수의 주소를 불러와 실행하게 해야한다. init()함수는 단순한 초기화 함수로 필요가 없다. 그렇기 때문에 위에 사진에서 보면 알다싶이 get_shell 주소를 찾았다. 지금 다시 위 코드를 보면 rsp를 0x30만큼 늘리고 sef는 0x8만큼의 크기를 가진다.









간단하게 입력값 두개를 받고 sub 함수를 통해 계산된 그 결과 값이 동일하며 correct 아니면 wrong이 나오는 그런 느낌이다.


해당 함수를 보면 입력값에 5를 곱하고 1400300배열과 동일한지 동일하면 correct가 나오는 값을 반환한다. 여기서 문제는 바이트값에 10진수 -5를 곱한다는점! 그리고 unsigned __int8은 형변환을 해줘서 3바이트를 버려준다.

배열 값은 위 사진을 보면 확인할수있다.

자 정리하자면 0xfb에 입력한 문자를 곱하고 배열이랑 비교한다.
그렇게 되면 입력한 문자는 배열에 나누기 0xfb를 하면된다.
그때의 나머지값이 0이면 입력한 문자가 flag임을 알수있다.
이때 -5와 곱한 문자와의 결과 값의 바이트가 넘어가는 경우는


입력할수 있는 문자는 52개이고 가장 큰 값인 z는 10진수로 122다.
이거를 이용해서 거꿀로 생각하는 코드를 짜서 돌리면 다음 사진과 같은 결과 가 나온다.

위 문자열을 그대로 base64로 디코딩하면 아래 사진과 같은 flag가 나온다


여기를 보면 text_in를 집어넣어서 아래 baseball 프로그램을 통해 걸쳐 나온 결과가 text_out으로 나온것같다. 입력값, 결과값을 알려줘서 특정 무언가를 구하게 한뒤 flag 결과값을 보여줬기에 그거를 반대로 돌려 계산하여 원래 flag 입력값을 찾아내는 식의 문제같았다.

baseball 파일을 들어가니까 ELF 파일인것같길래 그냥 리눅스 KAIL에서 ./baseball로 실행하거나 gdb 혹은 ida를 이용하면 될것같았다.

리눅스로 실행해보니까 두가지 인수값을 필요로 하는것을 알수있었다. 느낌상으로는 입력값의 파일과 출력값의 파일? 아님 입력값의 파일과 특정 계산에 필요한 인수값 파일?일것같았다.

여기서 슬펐던것은 main이 없는건지.. 심볼이 없는것이다. 이럴경우 어떻게 할지 몰라서 구글링해본결과 info file로 mian위치를? 확인하는 것같다. text가 main위치이기에

위 사진에서 text 위치에 break를 걸어서 실행시켰다.

잠깐 잘못 생각한게 인자 두개가 더 필요한데.. table에 관련된 정보가 없기에 r을 할수없다...

그래서 다르게 생각한게 strings로 baseball에 있는 main관련 문자열을 찾아보는것이다!!
다행이도 하나 문자열이 나와서 gdb에서 해당 disas main처럼 __libc_start_main을 할려고했다.
근데.. 실행이 되지 않았다. 이유는 모르겠다

두번째로 실행한 방법은 다른 tool을 사용하는 것이었다. 이툴은 리눅스 elf 파일을 불러와서 코드로도 보여주는 도구이다. 해당 툴로 열어본결과

main부분..을 찾았지만.. 뭔가 암호화를 한건지.. 패킹을 한건지 보이지 않았다. 코드 자체가 숨겨져 있다고 해야하나.. 결국 오랜시간 노력했지만 해당 코드 조차 보지 못했다..
결국 구글링을 통해 똑같은 baseball 문제가 존재하는 드림핵에서 코드를 가져왔다.. 정말 하면 안되지만.. 너무 궁금해서 코드만 가져왔다.
__int64 __fastcall main(int a1, char **a2, char **a3)
{
FILE *base64_table; // [rsp+10h] [rbp-30h]
__int64 size_of_base64_table; // [rsp+18h] [rbp-28h]
FILE *in_txt; // [rsp+20h] [rbp-20h]
__int64 size_of_in_txt; // [rsp+28h] [rbp-18h]
void *in; // [rsp+30h] [rbp-10h]
char *out; // [rsp+38h] [rbp-8h]
if ( a1 != 3 )
{
fwrite("Usage : ./baseball <table filename> <input filename>\n", 1uLL, 0x35uLL, stderr);
exit(-1);
}
base64_table = fopen(a2[1], "rb");
if ( !base64_table )
{
fwrite("File not found\n", 1uLL, 0xFuLL, stderr);
exit(-1);
}
fseek(base64_table, 0LL, 2);
size_of_base64_table = ftell(base64_table);
fseek(base64_table, 0LL, 0);
if ( size_of_base64_table != 64 )
{
fwrite("Invalid table\n", 1uLL, 0xEuLL, stderr);
exit(-1);
}
fread(&byte_4040, 0x41uLL, 1uLL, base64_table);
fclose(base64_table);
in_txt = fopen(a2[2], "rb");
if ( !in_txt )
{
fwrite("File not found\n", 1uLL, 0xFuLL, stderr);
exit(-1);
}
fseek(in_txt, 0LL, 2);
size_of_in_txt = ftell(in_txt);
if ( !size_of_in_txt )
{
fwrite("Invalid input\n", 1uLL, 0xEuLL, stderr);
exit(-1);
}
fseek(in_txt, 0LL, 0);
in = malloc(size_of_in_txt + 1);
if ( !in )
{
fwrite("Allocation failed\n", 1uLL, 0x12uLL, stderr);
exit(-1);
}
memset(in, 0, size_of_in_txt + 1);
fread(in, size_of_in_txt, 1uLL, in_txt);
fclose(in_txt);
out = (char *)encode(in, (unsigned int)size_of_in_txt);
printf("%s", out);
free(in);
free(out);
return 0LL;
}
위 코드를 해석해보니까
stream으로 파일을 불러온다. 그리고 그 파일내 값들의 길이가 64 인지 본다. 그리고 해당 파일을 바이트 주소값으로 읽어오는 함수를 실행하고 그 파일을 닫는다. 그리고 두번째 파일을 v6로 읽어온다. 그 파일의 길이가 있는지 없는지 확인하고 그 크기 +1만큼 동적할당한 s 변수를 만들고 v6의 값을 그 크기만큼 s에 읽어오는.. 옮겨오는 작업을 실행하는것같다. 그리고 두번째 파일을 닫는다. 그리고 두번째 파일에서 옮겨온 값 s를 이용하여 sub_1289라는 함수에 집어넣어 그값을 출력한다.. 이 출력값이 출력값같다.
sub_1289 함수를 찾아보니까
뭔가 크기 인수값을 이용하여 사이즈 변수를 다시 재정의하고 그 크기만큼 v13이라는 변수를 동적 할당한다. 그리고 s의 마지막 배열의 값을 v14에 넣고 v11에는 s의 주소값?을 넣는다. 그리고 v8에 동적할당한 주소값을 넣는다. 마지막 배열의 값에 s 주소값을 뺀 값이 2보다 크면 while문을 반복한다.
while문 부분이.. 너무나도 복잡한 결과를 보여줘서 살짝 힌트인것같은 문제 제목에 basebal에서 연상이 가능한 base64의 c코드를 봤다.
out[0] = (unsigned char) cb64[ (int)(in[0] >> 2) ];
out[1] = (unsigned char) cb64[ (int)(((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)) ];
out[2] = (unsigned char) (len > 1 ? cb64[ (int)(((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6)) ] : '=');
out[3] = (unsigned char) (len > 2 ? cb64[ (int)(in[2] & 0x3f) ] : '=');
위 코드가 base64의 코드인데 정말 비슷하다고 느끼는것이 2만큼 움직이고 6만큼움직이고 해당길이가 2이상인지등의 조건문등의 모습이 너무 비슷했다. 솔직히 반쯤 야매일수있지만 base64로 바꾸는 함수가 아닌가 싶었다. 아닐수도 있지만 base64와 baseball의 비슷함에 힌트라고 생각했기에 바로 그냥 base64로 text_in의 값을 돌려봤다.

아래의 사진을 보면 한눈에 봐도 대칭임을 알수있다. 1대1 대칭이기에 이거는 무조건 text_in값을 base64로 인코딩하여서 각각의 스펠링마다 대칭적으로 무슨 계산을 통해 바꾼것이다. 그렇기 때문에 flag_out 값도 해당 이 대칭값들을 이용해서 a->b로 바꿨다면 flag_out에도 똑같이 적용하기에 b->a로 바꾸면된다. 이러한 코드를 간단하게 작성해서 바꿔본결과


위 결과가 나왔고 base64 decoding 하자 flag가 나왔다.

다음 문제는 snow라는 사진을 준 문제다.

이 사진을 처음에는 아래 사진처럼 stegsovle 도구를 이용하여 색깔 반전등의 변화를 줘서 flag를 찾으려고 했다.

하지만 아무것도 나온게 없어서 HXD를 봤다.
시그니쳐를 보니까 JPG 시그니쳐길래 혹시 JPEG여서..뭔가 안보이는건가 했다. 그래서 JPG로 바뀐뒤에 열었는데 그대로였다.
다음으로는 xxd 명령어를 이용해서 문자열을 검색해봤다

flag 형식인 DH와 flag, {, }등 여러개의 문자열을 검색해 봤지만 의미있는것들은 확인할수없었다.

다음으로는 foremost를 이용하여 무언가 zip으로 압축된 파일이 나올까 했다. 하지만 output 파일에는 별 다른 것이 나오지 않았고 그냥 똑같은 snow 파일 그대로 나왔다.


가장 답에 가깝다고 생각한 stegsnow를 이용한 도구로 무언가 해볼려고 했는데 여러 옵션을 줘도 뭔가 보이는게 없었다..

그리고 사진의 이름과 비슷한 whitespace인가 싶엇지만.. hxd에서도 공백같은 특이점은 없었고 foremost로 나온 사진을 다시 hxd로 봐도 동일했다.. 결국 이 문제는 포기했다.
'UNITY > CTF' 카테고리의 다른 글
| SWING CTF v.02 풀이 (0) | 2022.11.12 |
|---|---|
| [w1] 시스템 해킹 (0) | 2022.11.02 |