Dreamhack/Dreamhack Wargame (Challenge)

[111] IT 비전공자 [dreamhack]Easy Assembly문제 풀기

imaginefuture-1 2024. 12. 30. 09:05

어셈블리~ 어셈블리도 공부해야하는데..!! 하다보면 알겠지!! ㅋㅋㅋㅋㅋㅋㅋㅋㅋ!! rsp~ rsi~

 

 

IDA로 구조를 한번보자

 

 

XOR 연산을 하는 것 같다

 

 

 

이 어셈블리 코드는 비밀번호 검증 루틴(check_password)을 나타내며, 두 개의 문자열(혹은 데이터 배열)을 비교하여 특정 조건을 만족하는지 확인하는 로직입니다. 아래에서 명령어 별로 분석하고 이 코드가 하는 작업을 설명하겠습니다.


코드 분석

초기 상태

  • esi: 입력된 데이터(비밀번호)의 시작 주소.
  • edi: 기준 데이터(예: 올바른 비밀번호)의 시작 주소.
  • eax: 비교할 데이터의 길이.
  • ecx: 결과를 저장하는 레지스터 (모든 연산의 결과를 통합).

코드 명령어 해석

  1. xor edx, edx
    • edx 레지스터를 0으로 초기화.
    • 목적: 이후 연산에 사용할 준비.
  2. mov dl, [esi]
    • **esi**가 가리키는 입력 데이터의 바이트를 **dl**로 복사.
    • **esi**는 입력 데이터의 현재 위치를 나타냄.
  3. xor dl, byte ptr ds:len
    • **len**은 전역 변수 또는 상수일 가능성이 큼.
    • **dl**에 저장된 값(입력 데이터의 현재 바이트)을 **len**과 XOR 연산.
  4. xor dl, [edi]
    • **edi**가 가리키는 기준 데이터의 바이트와 **dl**의 XOR 연산.
    • **edi**는 기준 데이터의 현재 위치를 나타냄.
  5. or ecx, edx
    • **edx**의 결과를 **ecx**에 OR 연산으로 통합.
    • 목적: 비교 과정에서 하나라도 차이가 있으면 **ecx**가 0이 아니게 됨.
  6. inc esi
    • **esi**를 증가시켜 입력 데이터의 다음 바이트로 이동.
  7. inc edi
    • **edi**를 증가시켜 기준 데이터의 다음 바이트로 이동.
  8. dec eax
    • **eax**를 감소시켜 남은 비교 바이트 수를 줄임.
  9. jnz short check_password
    • **eax**가 0이 아니면 루프를 반복. 즉, 모든 바이트를 비교할 때까지 반복.

요약

  1. 이 루프는 두 데이터 배열(입력 데이터와 기준 데이터)을 바이트 단위로 XOR 연산을 수행하면서 차이가 있는지 확인.
  2. 각 바이트는 len 값으로 먼저 XOR되고, 이후 기준 데이터와 XOR됨.
  3. ecx:
    • 모든 바이트가 동일하다면 최종적으로 0.
    • 하나라도 차이가 있으면 0이 아님.

사용 목적

이 루틴은 비밀번호 검증에 사용될 가능성이 큽니다:

  1. 입력된 비밀번호와 기준 비밀번호를 XOR 기반 비교.
  2. 만약 **ecx == 0**이라면, 두 비밀번호가 일치.
  3. 만약 **ecx != 0**이라면, 비밀번호가 일치하지 않음.

보안 약점

  1. XOR 기반 검증:
    • XOR 연산은 역연산이 쉬워서 보안성이 낮습니다.
    • 공격자가 메모리를 분석하면 len 값을 역추적하여 기준 데이터(비밀번호)를 쉽게 복구할 수 있습니다.
  2. 타이밍 공격 가능성:
    • 비교 과정이 입력 데이터와 기준 데이터의 차이에 따라 달라질 수 있음.
    • 이러한 정보를 기반으로 비밀번호를 유추할 수 있습니다.

이 코드는 학습 목적으로 제공된 것일 가능성이 크며, 실제로 사용하기에는 보안성이 낮습니다.

 

흐흠 시작 주소가 보이는군, 요건 GDB 사용해서 더 자세히 봐야할 것 같다

 

 

 

는 바로 디컴파일러 돌려버리기~ ㅋㅋㅋㅋㅋ

 

check_password()
맛있게 들어있는 enc_flag 값들 ㅋㅋㅋㅋ

 

data:0804A000 ; _BYTE enc_flag[49]
.data:0804A000 enc_flag        db 74h, 78h, 4Bh, 65h, 77h, 48h, 5Ch, 69h, 68h, 7Eh, 5Ch
.data:0804A000                                         ; DATA XREF: LOAD:0804807C↑o
.data:0804A000                                         ; _start+35↑o
.data:0804A00B                 db 79h, 77h, 62h, 46h, 79h, 77h, 5, 46h, 54h, 73h, 72h
.data:0804A016                 db 59h, 69h, 68h, 7Eh, 5Ch, 7Eh, 5Ah, 61h, 57h, 6Ah, 77h
.data:0804A021                 db 66h, 5Ah, 52h, 2, 62h, 5Ch, 79h, 77h, 5Ch, 0, 7Ch, 57h
.data:0804A02D                 db 2 dup(0Dh), 4Dh, 0

 

 

이 코드는 어셈블리 코드나 디컴파일된 코드에서 나온 것으로 보이며, 디컴파일러에서 스택 포인터 값이 양수로 감지되었다는 경고와 함께 출력된 내용입니다. 아래에서 코드를 분석하고, start 함수가 수행하는 작업과 의미를 설명하겠습니다.


코드 분석

1. 함수 정의 및 주요 변수

void __noreturn start()
  • __noreturn: 이 함수는 반환되지 않습니다. 즉, exit() 또는 유사한 시스템 호출을 사용하여 프로그램을 종료합니다.
  • 로컬 변수:
    • v2: 스택에서 가져온 값으로, 조건 분기에서 사용.
    • v0: ecx 레지스터를 기반으로 초기화된 변수로, 특정 데이터(예: 사용자 입력, 버퍼 등)와 관련된 것으로 보임.

2. 조건 분기 (if (v2 == 1))

if (v2 == 1)
{
    print();
    print();
}
  • 스택 변수 **v2**의 값이 1이면 print() 함수가 두 번 호출됩니다.
    • print() 함수의 동작은 정의되지 않았지만, 주로 출력과 관련된 작업일 가능성이 큽니다.

3. else 분기 (비밀번호 확인 및 플래그 암호화 해제)

else
{
    len = strlen();
    check_password(len, 0, enc_flag, v0);
}
  • strlen() 호출:
    • 문자열의 길이를 계산. (아마도 사용자 입력 문자열의 길이로 추정)
    • 반환값은 변수 len에 저장.
  • check_password(len, 0, enc_flag, v0):
    • check_password 함수는 암호 검증을 수행.
    • 매개변수:
      • len: 문자열의 길이.
      • 0: 이 값은 특정 옵션이나 상태를 나타낼 가능성이 있음.
      • enc_flag: 암호화된 플래그 데이터.
      • v0: 사용자 입력(또는 버퍼)와 관련된 포인터.
    • 이 함수는 입력값과 암호화된 플래그 데이터를 비교하거나 연산하여 플래그를 복호화하는 로직으로 보임.

4. print() 호출 및 종료

print();
v1 = sys_exit(0);
  • print(): 무조건 호출되어 어떤 데이터를 출력.
  • sys_exit(0): 프로그램 종료.
    • **sys_exit**는 리눅스 시스템 호출로, 프로그램을 종료하고 반환값으로 0을 설정.

코드 요약

  1. v2 == 1인 경우:
    • print() 함수가 두 번 호출된 후 프로그램 종료.
  2. 그 외 경우:
    • 사용자 입력의 길이를 계산한 후, check_password 함수로 암호화된 플래그를 복호화.
    • **print()**를 호출하여 데이터를 출력 후 프로그램 종료.

의미와 분석

스택 변수와 경고 메시지

// positive sp value has been detected, the output may be wrong!
  • 이 경고는 디컴파일러가 스택 프레임 복구 과정에서 스택 포인터(sp)의 값을 잘못 해석했음을 의미.
  • 주로 스택 오프셋이 양수로 계산될 때 발생:
    • 예: 함수 호출 및 반환 후의 오프셋 계산 오류.
  • 실제 코드 실행에는 영향이 없지만, 디컴파일된 코드를 분석할 때 잘못된 변수 위치나 값을 볼 수 있음.

공격 관점

  1. check_password 취약점 탐색:
    • 사용자 입력(v0)과 암호화된 플래그(enc_flag) 간의 연산이나 비교가 이루어짐.
    • check_password 함수의 동작을 분석하여 암호화된 플래그 복호화 가능성을 확인.
  2. print()의 데이터 유출 가능성:
    • 함수가 암호화된 플래그나 입력 데이터를 출력할 가능성을 탐색.
    • 출력된 데이터가 플래그 복호화에 유용한 단서를 제공할 수 있음.
  3. 스택 변수(v2)의 조작 가능성:
    • 스택 변수 v2를 조작하여 조건문 분기를 강제로 변경.
    • 디버거를 사용하여 스택의 값을 변경하거나, 스택 오버플로우를 이용한 취약점 활용 가능성.

결론

이 코드는 비밀번호 검증 및 플래그 복호화 과정을 포함하고 있으며, 다음을 중심으로 분석을 진행해야 합니다:

  1. check_password 함수의 동작 분석.
  2. print() 함수의 출력 데이터 확인.
  3. 스택 변수(v2) 조작 가능성.

플래그를 얻으려면 check_password의 동작을 재현하거나, 플래그와 관련된 데이터를 출력하도록 로직을 조작해야 합니다.

 

 

암호화 과정 : enc_flag[i] ^ len ^ key[i] 
xor의 특징 중 A = B ^ C는 B = A ^ C라는 특성이 있습니다.
이 특성을 이용하면 key를 구할 수 있게 됩니다. 
result = enc_flag[i] ^ len ^ key[i]
0 = enc_flag[i] ^ len ^ key[i]
※ result가 0인 이유는 _start 함수에서 0이 떠야 print(correct_msg);를 출력해 주기 때문입니다.
  즉 다시 한번 수식을 정리하자면 아래와 같습니다.
key[i] = enc_flag[i] ^ len

출처:https://tjdmin1.tistory.com/7[Tjdmin1:티스토리]

 

enc_flag[i] =  위에 얘기했던 값들이 있고

len은 문자배열 길이다 -->요건 좀따 더 공부해야할뜻

분석

위 코드는 XOR 연산을 사용하여 암호화된 데이터를 복호화하는 코드입니다. 해당 코드에서 사용된 방식과 의미를 하나씩 분석하겠습니다.


코드 설명

  1. 암호화된 데이터 enc_flag:
    • 이 배열은 암호화된 플래그 데이터입니다.
    • 각 값은 XOR 연산을 통해 암호화된 바이트를 나타냅니다.
  2. enc_flag = [ 0x74, 0x78, 0x4B, 0x65, 0x77, 0x48, 0x5C, 0x69, 0x68, 0x7E, 0x5C, 0x79, 0x77, 0x62, 0x46, 0x79, 0x77, 0x05, 0x46, 0x54, 0x73, 0x72, 0x59, 0x69, 0x68, 0x7E, 0x5C, 0x7E, 0x5A, 0x61, 0x57, 0x6A, 0x77, 0x66, 0x5A, 0x52, 0x02, 0x62, 0x5C, 0x79, 0x77, 0x5C, 0x00, 0x7C, 0x57, 0x0D, 0x0D, 0x4D ]
  3. 복호화 과정:
    • len(enc_flag): enc_flag 배열의 길이를 나타냄. 이 값은 XOR 연산에서 키로 사용됩니다.
    • enc_flag[i]: 암호화된 데이터의 i번째 바이트.
    • len(enc_flag) ^ enc_flag[i]: 암호화된 바이트와 키를 XOR 연산하여 복호화된 문자를 얻음.
    • chr(): ASCII 값(정수)을 문자로 변환.
  4. for i in range(len(enc_flag)): print(chr(len(enc_flag) ^ enc_flag[i]), end='')
  5. XOR 연산의 특징:
    • XOR의 대칭성: A ^ B ^ B = A
      • XOR 연산을 두 번 적용하면 원래 값이 복원됩니다.
    • 암호화 시:
      enc_flag[i] = original_char ^ key
      
    • 복호화 시:
      original_char = enc_flag[i] ^ key
      
  6. len(enc_flag):
    • XOR 키로 사용되는 값으로, enc_flag 배열의 길이(49)가 됩니다.
  7. 출력:
    • 모든 바이트를 복호화하여 하나의 문자열로 출력.

출력 예제

아래 코드는 복호화된 결과를 출력합니다.

enc_flag = [
    0x74, 0x78, 0x4B, 0x65, 0x77, 0x48, 0x5C, 0x69, 0x68, 0x7E, 0x5C,
    0x79, 0x77, 0x62, 0x46, 0x79, 0x77, 0x05, 0x46, 0x54, 0x73, 0x72,
    0x59, 0x69, 0x68, 0x7E, 0x5C, 0x7E, 0x5A, 0x61, 0x57, 0x6A, 0x77,
    0x66, 0x5A, 0x52, 0x02, 0x62, 0x5C, 0x79, 0x77, 0x5C, 0x00, 0x7C,
    0x57, 0x0D, 0x0D, 0x4D
]

for i in range(len(enc_flag)):
    print(chr(len(enc_flag) ^ enc_flag[i]), end='')

결과

복호화된 플래그를 출력합니다:

Decrypted Flag: DH{...}

출력은 플래그 형식 DH{}으로 시작하는 문자열입니다.

 

 

 

 

하지말랍니다...ㅋㅋㅋㅋㅋ

 

 

https://tjdmin1.tistory.com/7

 

[Dreamhack] Easy Assembly

https://dreamhack.io/wargame/challenges/1095 Easy Assembly어셈블리 언어에 대한 지식이 있으시다면 이 문제는 쉽게 해결할 수 있는 아주 간단한 문제입니다! 필요한 정보만을 찾아 플래그를 획득해 주세요

tjdmin1.tistory.com

 

https://successing.tistory.com/73

 

dreamhack wargame Easy Assembly

문제 설명어셈블리 언어에 대한 지식이 있으시다면 이 문제는 쉽게 해결할 수 있는 아주 간단한 문제입니다.필요한 정보만을 찾아 플래그를 획득해주세요. step1. main(or start)함수 분석설명)위에

successing.tistory.com

 

https://h-factory.tistory.com/754

 

[Dreamhack] Easy Assembly

권한을 주고 실행하니 이런 메시지가 나왔다. 실행파일로 만들어야된다. cat 명령어로 보니 안에 이런 문자들이 있었다.안에도 Usage : 이 부분이 있어서 다음 문자열을 ./prob 실행할 때 같이 넣어

h-factory.tistory.com