촉촉한초코칩
angstromCTF 2024 본문
Guess the Flag - 해결
objdump나 ghidra와 같은 프로그램을 사용하여 바이너리를 실행할 때 바이너리가 무엇을 하는지 검사하라고 되어 있다.
먼저 해당 프로그램이 뭔지 찾아보았다.
objdump
- 라이브러리, 컴파일된 오브젝트 모듈, 공유 오브젝트 파일, 독립 실행파일 등의 바이너리 파일들의 정보를 보여준다.
- objdump는 elf 파일을 어셈블리어로 보여주는 디스어셈블러로 사용될 수 있다.
- objdump -f <filename> : 파일 헤더 정보
- objdump -d <filename> | grep \<함수명\>: -A <출력 라인 수> : 실행 영억 중 원하는 영역만 볼 때
- objdump -D <filename> : 파일의 모든 영역을 디스어셈블 해준다.
ghidra
- 미국 국가안보국에 의해 개발된 엔지니어링 프레임워크
- 윈도우에서 설치, 실행이 가능한 것 같다.
→ ghidra를 사용해서 분석을 진행해 보았다.
메인함수
undefined8 main(void)
{
int iVar1;
size_t sVar2;
byte *pbVar3;
long in_FS_OFFSET;
byte local_68 [72];
long local_20;
local_20 = *(long *)(in_FS_OFFSET + 0x28);
puts("Go ahead, guess the flag: ");
fgets((char *)local_68,0x3f,stdin);
pbVar3 = local_68;
while( true ) {
sVar2 = strlen((char *)local_68);
if (sVar2 <= (ulong)((long)pbVar3 - (long)local_68)) break;
*pbVar3 = *pbVar3 ^ 1;
pbVar3 = pbVar3 + 1;
}
iVar1 = strcmp((char *)local_68,secretcode);
if (iVar1 == 0) {
puts("Correct! It was kinda obvious tbh.");
}
else {
puts("Wrong. Not sure why you\'d think it\'d be that.");
}
if (local_20 == *(long *)(in_FS_OFFSET + 0x28)) {
return 0;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
- local_68에 63만큼 입력값을 받고 그 값을 pbVar3에 넣는다.
local_68을 char형으로 입력받고, pbVar3은 byte형이기 때문에 형변환이 일어난다. - while문이 true일 때 입력값의 길이를 재서 sVar2에 넣는다.
- 그리고 입력값을 byte형식으로 저장된 pbVar3과 char형식으로 저장된? local_68을 뺴서 local_68의 길이와 비교한다.
만약 sVar2의 크기가 더 크면 끝내고,
sVar2의 크기가 더 작다면 1을 곱하고 더한다. > 값을 계속해서 늘려서 while문을 빠져나가기 위함인것 같다. - 그리고 char형으로 바꾼 local_68과 secretcode를 비교해서 둘의 값이 같다면, 통과하는 것 같다.
그렇다면, secretcode를 알아내야 한다.
- 해당 부분을 보니 secretcode의 주소값을..? RSI에 넣고 있다.
- 그리고 strcmp 함수를 실행하기 위해 MOV 명령어를 실행하고,
- eax와 eax의 값을 AND 연산해서 둘의 값이 0이 아니라면 0010117e (else 문)으로 이동한다.
- 아래 값들을.. 다 적어보았다.
\x60\x62\x75\x67\x7a\x62\x6e\x6c\x6c\x68\x75\x75\x64\x65\x5e\x75\x6e\x5e\x75\x69\x64\x5e\x6d\x64\x60\x72\x75\x5e\x72\x68\x66\x6f\x68\x67\x68\x62\x60\x6f\x75\x5e\x63\x68\x75\x7c\x00
60 62 75 67 7a 62 6e 6c 6c 68 75 75 64 65 5e 75 6e 5e 75 69 64 5e 6d 64 60 72 75 5e 72 68 66 6f 68 67 68 62 60 6f 75 5e 63 68 75 7c 00
음.. `bugzbnllhuude^un^uid^md`ru^rhfohghb`ou^chu| ..?
search > for strings에서 secretcode를 찾았으나.. 정답은 아닌 듯함
strcmp 함수에서는 s1와 s2를 비교하길래 이때 RDI와 RSI의 값을 알아보려고 한다.
그런데 ghirda 프로그램에서 저 파일을 실행하는 방법을 못 찾겠어서 pwndbg로 실행해보았다.
이 부분에 break를 걸고 실행해서 rsi나 rdi의 값을 확인한다.
일단 correct가 나오게 하는 secretcode는 저게 맞았고... 입력하고 나니까 flag가 보인다.....
처음에 flag는 RBP에 있다.
그리고 mov rdi, rbp를 실행한다.
strcmp에서 rdi는 s1, 즉 flag를 의미한다.
정확한 분석은 잘 못하겠으나... 이렇게 비교한다는 건 대충 알 것 같다..
근데 `bugz.. 이 문자열이 어떻게 actf{와 동일하다고 판단되는 건지는.. 의문이다.
지금 보니 위에도 secretcode가 rsi에 있다는 것을 내가 적었는데,
ghidra에서 lea rsi, [secretcode]가
pwndbg에서는 lea rsi, [rip+0x2ed3] 이렇게 표현되는 것 같다.
나중에도 ghidra를 써봐야겠다..
spinner - 해결
message 함수 부분을 보면
- state의 flagged가 true라면 element에 message의 값을 넣고
- element(즉, message)에 state.total / 360한 값을 넣는다.
- 만약 나눈 값이 10_000 * 360 보다 크거나 같다면 flag가 나오는것 같다.
의문
- 10_000 : 숫자 나타낼 때 , 사용하는 거랑 동일하다고 함
- await : await 뒤에 있는 값?이 처리될 때까지 기다린다. (response.text는 html 값을 받아온다고 한다..)
- async : 결과값으로 message()를 반환한다.
결론
- state.total의 값이 10000*360한 값보다 커야 한다.
- state.flagged의 값이 true가 되어야 한다.
분명.. 처음에는 state.total에 값도 넣고 flagged에도 true 넣어서 message 실행할 땐 안됐는데....
다시 하니까 됨...
- 사실 flag가 나오는 부분에서 체크하는 건 total의 값이니까 flagged의 값은 변경 안 해도 상관없다.
- 그리고 message를 실행하면 나온다..
??? burp suite 가지고.. 난리였는데
암튼 flag 찾았다..
나중에 읽어보기 → https://joyful-development.tistory.com/20
'CTF' 카테고리의 다른 글
NahamConCTF 2024 (0) | 2024.05.24 |
---|---|
BYUCTF 2024 (0) | 2024.05.17 |
VolgaCTF 2024 (0) | 2024.03.31 |
JerseyCTF 2024 (0) | 2024.03.24 |
WolvCTF 2024 (0) | 2024.03.17 |