wake me up inside

Kategori

Rev

Beskrivning

Kenneth har börjat känna sig lite ensam och har försökt skapa en robot för att ha som sällskap och spela lite brädspel med. Men han fick inte riktigt till det, för roboten är ledsen hela tiden, oavsett vad han försöker ge den för input.

Harriet har kommit över mjukvaran till roboten, och försökt få den på gott humör. För om Kenneth har någon att lira Terraforming Mars med så kanske han tappar intresset för sin plan att ta över universum.

Men hon kan inte heller få roboten att dra på smilbanden. Kan du göra Kenneths robot glad?

Lösning

Öppnar vi den bifogade filen wake_me_up_inside_024fa20249d9c3fffa2582a582f995d9 i IDA, så får vi en stor binär med statiskt länkade funktioner från exemepelvis OpenSSL.

I funktionen start kan vi hitta originalfunktionen för main.

// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3)
{
  __int64 v3; // rax
  int v4; // esi
  __int64 v5; // [rsp-8h] [rbp-8h] BYREF
  _UNKNOWN *retaddr; // [rsp+0h] [rbp+0h] BYREF

  v4 = v5;
  v5 = v3;
  sub_621890((unsigned int)sub_402EC0, v4, (unsigned int)&retaddr, 0, 0, a3, (__int64)&v5);
  __halt();
}

Här ser vi att sub_402EC0 är originalfuktionen. Öppnar vi den så kan vi fortsätta vår analys.

__int64 __fastcall sub_402EC0(__int64 a1, int a2)
{
  int v2; // edx
  int v3; // ecx
  int v4; // r8d
  int v5; // r9d
  __int64 result; // rax
  int i; // [rsp+8h] [rbp-38h]
  unsigned int v8; // [rsp+Ch] [rbp-34h]
  _BYTE v9[40]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v10; // [rsp+38h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  sub_402E0A();
  sub_62B480((unsigned int)"> ", a2, v2, v3, v4, v5);
  sub_63A020(v9, 27, off_844BD8);
  sub_63ABD0(&unk_70A01E);
  for ( i = 0; i <= 25; ++i )
  {
    v8 = sub_402CC5(&v9[i], 1) + dword_842140[i];
    sub_689960(1);
    sub_689990(v8);
  }
  sub_63ABD0(":)");
  result = 0;
  if ( v10 != __readfsqword(0x28u) )
    sub_6925D0();
  return result;
}

Nu kan vi köra programmet för att se vad som händer.

$ ./wake_me_up_inside_024fa20249d9c3fffa2582a582f995d9
> TEST

:(

Nu vet vi att sub_62B480 skriver ut “> “. sub_63A020 verkar vara en funktion som läser in input från användaren. sub_63ABD0 verkar vara en funktion som skriver ut text till användaren.

Vi kan inte enkelt se vad sub_402E0A och funktionerna i for-loopen gör. Vi kan börja med att analysera for-loopen där vår input används.

Debuggar vi programmet och kollar vad sub_402CC5 gör så ser vi att den tar en char, MD5-hashar den och sedan plockar ut delar av hashen.

__int64 __fastcall sub_402CC5(__int64 a1, unsigned int a2)
{
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 result; // rax
  unsigned int v5; // [rsp+10h] [rbp-20h] BYREF
  unsigned int v6; // [rsp+14h] [rbp-1Ch]
  __int64 ctx; // [rsp+18h] [rbp-18h]
  unsigned __int8 *v8; // [rsp+20h] [rbp-10h]
  unsigned __int64 v9; // [rsp+28h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  v2 = get_md();
  v5 = EVP_MD_get_size(v2);
  v6 = 0;
  ctx = EVT_create_ctx();
  v3 = get_md();
  EVP_md_init(ctx, v3, 0);
  EVP_DigestUpdate(ctx, a1, a2);
  v8 = (unsigned __int8 *)malloc(v5, "wake_me_up_inside.c", 26);
  EVP_DigestFinal_ex(ctx, v8, &v5);
  sub_403CE0(ctx);
  v6 |= v8[12] << 24;
  v6 |= v8[13] << 16;
  v6 |= v8[14] << 8;
  v6 |= v8[15];
  result = v6;
  if ( v9 != __readfsqword(0x28u) )
    stack_smashing_detected();
  return result;
}

Raden som resultatet från funktionen sub_402CC5 används på läser även in en int från en array dword_842140 och tilldelar det till v8. Värdena i dword_842140 är:

.data:0000000000842140 dword_842140    dd 0B0DE2CB4h, 4CEC9C5Fh, 0E91F6E53h, 0B0DE2CB4h, 949A9B62h
.data:0000000000842140                                         ; DATA XREF: sub_402EC0+91↑o
.data:0000000000842154                 dd 0CA9BBAFAh, 3 dup(6FB0CBA3h), 9688D99Fh, 0B319FFCEh
.data:000000000084216C                 dd 0D3CF0FCCh, 4CEC9C5Fh, 2 dup(6789B26h), 0F7BAE229h
.data:0000000000842180                 dd 17BE13CEh, 0B319FFCEh, 6FB0CBA3h, 9688D99Fh, 0B319FFCEh
.data:0000000000842194                 dd 4DFEC4CDh, 6789B26h, 0F7BAE229h, 0D58450Dh, 55FB6A31h
.data:00000000008421A8                 dd 6 dup(0)

Nästa funktion, sub_689960, verkar vara en wrapper för sys_alarm.

unsigned __int64 __fastcall sub_689960(unsigned int a1)
{
  unsigned __int64 result; // rax

  result = sys_alarm(a1);
  if ( result >= 0xFFFFFFFFFFFFF001LL )
  {
    __writefsdword(0xFFFFFFA8, -(int)result);
    return -1;
  }
  return result;
}

sub_689990 som är nästa funktion gör en hel del andra intressanta saker.

__int64 __fastcall sub_689990(unsigned int a1)
{
  unsigned int v1; // ebp
  __int64 result; // rax
  const struct timespec *v3; // [rsp+0h] [rbp-38h] BYREF
  __int64 v4; // [rsp+8h] [rbp-30h]
  unsigned __int64 v5; // [rsp+18h] [rbp-20h]

  v5 = __readfsqword(0x28u);
  v1 = __readfsdword(0xFFFFFFA8);
  v3 = (const struct timespec *)a1;
  v4 = 0;
  if ( (int)sub_689A00((const struct timespec *)&v3, (struct timespec *)&v3) < 0 )
  {
    result = (unsigned int)v3;
  }
  else
  {
    __writefsdword(0xFFFFFFA8, v1);
    result = 0;
  }
  if ( v5 != __readfsqword(0x28u) )
    stack_smashing_detected();
  return result;
}

Vilket i sin tur anropar sub_689A00.

__int64 __fastcall sub_689A00(const struct timespec *a1, struct timespec *a2)
{
  __int64 result; // rax

  result = sub_6E6FD0(0, 0, a1, a2);
  if ( (_DWORD)result )
  {
    __writefsdword(0xFFFFFFA8, result);
    return 0xFFFFFFFFLL;
  }
  return result;
}

Som i sin tur anropar sub_6E6FD0.

__int64 __fastcall sub_6E6FD0(
        clockid_t clock_id,
        unsigned int a2,
        const struct timespec *timespec1,
        struct timespec *timespec2)
{
  unsigned int v4; // r13d
  const struct timespec *v5; // r12
  bool v7; // bl
  int v8; // ebp
  int v9; // r15d
  unsigned int v10; // r15d
  int v12; // r8d
  __int64 v13; // rax
  int v14; // r8d
  _QWORD v15[2]; // [rsp+10h] [rbp-68h] BYREF
  __m128i v16; // [rsp+20h] [rbp-58h] BYREF
  unsigned __int64 v17; // [rsp+38h] [rbp-40h]

  v17 = __readfsqword(40u);
  if ( clock_id == 3 )
  {
    v10 = 22;
  }
  else
  {
    v4 = a2;
    v5 = timespec1;
    if ( clock_id == 2 )
    {
      v7 = 0;
      clock_id = -6;
    }
    else
    {
      v7 = clock_id == 0;
    }
    if ( __readfsdword(24u) )
    {
      sub_6DAA40();
      v8 = sys_clock_nanosleep(clock_id, a2, v5, timespec2);
      sub_6DAAB0(v12);
    }
    else
    {
      v8 = sys_clock_nanosleep(clock_id, a2, timespec1, timespec2);
    }
    v9 = v8;
    if ( v8 != -22 || !v7 )
      goto LABEL_8;
    v10 = 22;
    if ( v5->tv_nsec <= 999999999uLL && v5->tv_sec >= 0 )
    {
      if ( (a2 & 1) == 0 )
      {
        if ( !__readfsdword(0x18u) )
        {
LABEL_17:
          v9 = sys_clock_nanosleep(1, v4, v5, timespec2);
LABEL_8:
          v10 = -v9;
          goto LABEL_9;
        }
LABEL_23:
        sub_6DAA40();
        v9 = sys_clock_nanosleep(1, v4, v5, timespec2);
        sub_6DAAB0(v14);
        goto LABEL_8;
      }
      v16 = _mm_loadu_si128((const __m128i *)v5);
      if ( !(unsigned int)sub_689400(0, v15) )
      {
        v13 = v16.m128i_i64[1] - v15[1];
        v16.m128i_i64[1] = v13;
        if ( v13 < 0 )
        {
          v16.m128i_i64[0] = v16.m128i_i64[0] - v15[0] - 1;
          v16.m128i_i64[1] = v13 + 1000000000;
        }
        else
        {
          v16.m128i_i64[0] -= v15[0];
        }
        v5 = (const struct timespec *)&v16;
        v4 = a2 & 0xFFFFFFFE;
        if ( !__readfsdword(0x18u) )
          goto LABEL_17;
        goto LABEL_23;
      }
    }
  }
LABEL_9:
  if ( v17 != __readfsqword(0x28u) )
    stack_smashing_detected();
  return v10;
}

Utifrån detta kan vi se att programmet sover i lika många sekunder som resultatet av hashnings-funktionen returnerar, adderat med värdet från tabellen.

Detta betyder att vi behöver beräkna den modifierade MD5-hashen för varje bokstav. Är värdet av hashen adderat med värdet från tabellen noll, är det en korrekt bokstav.

Vi kan nu skriva ett skript som gör detta.

#!/usr/bin/env python3

import hashlib
import string

alphabet = string.printable

table = [0x0B0DE2CB4, 0x4CEC9C5F, 0x0E91F6E53, 0x0B0DE2CB4, 0x949A9B62, 0x0CA9BBAFA, 0x6FB0CBA3, 0x6FB0CBA3, 0x6FB0CBA3, 0x9688D99F, 0x0B319FFCE, 0x0D3CF0FCC,
         0x4CEC9C5F, 0x6789B26, 0x6789B26, 0x0F7BAE229, 0x17BE13CE, 0x0B319FFCE, 0x6FB0CBA3, 0x9688D99F, 0x0B319FFCE, 0x4DFEC4CD, 0x6789B26, 0x0F7BAE229, 0x0D58450D, 0x55FB6A31]


def hash(s):
    md5 = hashlib.md5(s)
    digest = md5.digest()
    v6 = 0
    v6 |= digest[12] << 24
    v6 |= digest[13] << 16
    v6 |= digest[14] << 8
    v6 |= digest[15]
    return v6


flag = ''

for v in table:
    for c in alphabet:
        h = hash(c.encode())
        if (h + v) & 0xFFFFFFFF == 0:
            print(f"Found: {c}")
            flag += c
            break

print(f"Flag: {flag}")

Kör vi skriptet får vi flaggan undut{yyya_sn00ze_ya_l0z3}.

n00bz

Home of the n00bz CTF team.


By n00bz, 2025-03-29