We participated the APU BOH yesterday! It was quite fun for beginners, I think it was better compared to last year.

Scoreboard:

scoreboard

Here is some writeups of some reverse challenges!

Challenges

babyrev

Challenges file

We have two files given, one linux executable file (ELF), and one 7zip file which need password to extract it

Try running the babyrev can see it need an argument:

./babyrev
Need exactly one argument.

./babyrev asdf
Incorrect.

We can see it say incorrect, can guess the correct argument will be the password of the 7zip file

Running strings can see got a very long string:

strings babyrev

/lib64/ld-linux-x86-64.so.2
puts
__cxa_finalize
__libc_start_main
libc.so.6
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u3UH
[]A\A]A^A_
Need exactly one argument.
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

Looks like base64, tried to decode it but failed, lets open it with decompiler - Ghidra

undefined8 FUN_00101139(int param_1,long param_2)

{
  undefined8 uVar1;
  int local_14;
  
  if (param_1 == 2) {
    local_14 = 0;
    while (((&DAT_00102028)[local_14] != '\0' &&
            (*(char *)((long)local_14 + *(long *)(param_2 + 8)) != '\0'))) {
      if ((char)(&DAT_00102028)[local_14] + -0xf !=
           (int)*(char *)((long)local_14 + *(long *)(param_2 + 8))) {
         puts("Incorrect.");
         return 1;
      }
      local_14 = local_14 + 1;
    }
    puts("Correct!");
    uVar1 = 0;
  }
  else {
    puts("Need exactly one argument.");
    uVar1 = 0xffffffff;
  }
  return uVar1;
}

Can see it compare our argument param_2 + 8 with the long string DAT_00102028

The comparision happen at this line:

if ((char)(&DAT_00102028)[local_14] + -0xf != (int)*(char *)((long)local_14 + *(long *)(param_2 + 8))) {
	puts("Incorrect.");
	return 1;
}

Basically means the ASCII value of the long string minus 0xf if not equal our argument then it prints incorrect

Also means the long string minus 0xf is the correct argument we need!!

Wrote a simple python script to do this:

text = b"TzgBGgWNsQLHRLY..."
for t in text:
	print(chr(t-0xf),end='')

Output:

EkX38XH?dB=9C=JG2D)$[2Ke6T_AT;F*g%DBYb'f;9b?Xi%39'FIV%gSFA=$eWXW;R\J>aRdi?Y6_dX*)&%7KI9>I7>kIYe>\S=4>C)W<S&#b\SGa7I'TdSK)W_W($8$#(CK>)8V$TD823#j(iYjG2i8)=8(*<5ic==eB_aDaY2CBAJgF%E5<$dS%Ci[;2<;%?U;EK__=_cHY_hY5Y93'W64d9J%h7#48FD=faKUf?e<7G'G%a7F8fe;F>(&fa$fU746^=8f6ggb5A&&DGE_=&j\3g(_?=if&XeT_k'5k4RKb*^?fcGfB_I3fDg[AdBK?E^eEk#D*((k548Y3dgh?Aa_5eH8(8EkCX%Ccf[$Rg>d&eKFI5bBhD9X8==h722#>jR^ka[R(Y3i\$c'D>RGC2<UgC34237BAiIfB^DY$E^[>V3US%h7c4bVCBbU&T9=U'hK#HXK[X5FVjUf_KHAJ%T_gRHJd'[939EX$TJ)_>a?)$%H<%B;Sd7?a3=aH$'g7IEJR_kIkRIkK=#V<_d5(Df;XWjCD<Y%GeY*UU3%FB3>%3iF8>c==U_^eJ#3j9=U4&eXG94^'7_bTD548=eS_Rcd&=AE>))$AFeBX$&hS2\4\?8G67D&gcS?DdR*EK^?XT*D5j>2b<iieiCIG3X'*(j9;$cT#k3e\ekW6cJDJ7e76DG4ABHkD7Sg&;<YTc%$SbcIB7EHEY7_EbEfIJe87eVki78WBW;RHR*H>&X\$$9DGH=f4CUJT2g$IaV[EA7D9H'E$;c%j[J;?[UBKbJhW=&#&>b$)S$SB28;^XBE_2'D#&&4>6)*cRDhhX$\Dc$JjRAK)<VgC)XFRdk_[2%(J5^d9[U#HHfS$GA&D?j>Ta$KC[?kdiG[Y^'8V)hdJT(kWJD^K>GH;'kA<>#iYaS#W3I'G([Rb&*B4c4$JJIE5=&j>*4>=S(=X(c$;<d5UG9=&RbXi_kJK5A<;ASj\(*=T)=i34^FK\(7Egb3[>#)R2$H2Kf7FSSB%7<?<7RX3&XW\3%EW<D9EFA27J>HDRAdT3Sb5>W)$c)^d$F;<6*^YU3FjGA;fec#YJ>W$IiWd5hG_9(^&6<k[ka9%KER4^bUY?6Ek48$#h4e=b345kbc9';93ITKFHVe8Kh&9_*[>)S?)h)*6%U%)\ak3#>[5D=53aX2g9FKT=T'87_$)I>7dj^B%)K%HB5jh)g\H(3*9a6&_U#4A^6GVg^*G4f(<*86BXhBaI>8SY#KHbb^[7)AERgh#djaB#4&bVi#ai8)EK7=a5fUG9Vc3[)KIF%>KeA'X>R_aa7DD>ASY*Ce4B(US4)Rf4[Bif=>GS6*R(5C^2H48_7kJi>g=;>&fId\fj%j#eI?RG%G4WfA^9C>%da\#A^Xc2'32##)8_^_bCif;f\kG7I)WC3BRd>gg7hKKWD*eB>&dEYI2)\&G;kJDaJhKK=cWUHiDKR)beUc8*=C4TIkKIadR78Dg%&B7c#a&^#^W)[63^%*S;>_&5[ACJDe*2>kJRXVUEA=B9IacIgIXa$?%c#89>f6#4d44'Ji*h;<9FEkS#IbgTS6?6#*T7H;BF%#%H2'dghKa#(6[Vj\&k$gA?H#XFdI(9(T?;2b;Ha'^WjE=?8K\)BUV4)<\R_&W&3A>$S_ibX__##(Bb'%d'bV$<D<W9I*Ed4fB;c6<Cb)X65Jb$[I2[)<U)&6=GGB53[^2b?=eaJRV3kH43k#^eBB*3h=kAU<)>fgR%Cf7>F&8W4%BKe3TX33BDgV2ef3V[&k*'#T<(9Uc7(G47k9&XeaiF'=)89\IfD%GY?d74?Da7<89?\%_eiSi9fa#IH5TBi;g_T7?G2KfbK%$#)$=4YAae$4<_*$*A7\ec6fU6'#$);&2a\&HK=2h<A_d&W)Ad6hRSG__;f%C*=9>2iJ4\eG\=BDVYURiT8dG5JVVYb<j9VfF7K[?I\5B)>T>^b6J$6IA9JeAHSiaE3Yb9$4KSWAT$B2%SUb?#ERR['[eC&2<A9ISee5A5jU=>d4U>7hU#$7;$%g36aT8RW;<)I9[[W4

The output looks like some weird encryption but it is not I tried to decode

It is the password of the 7zip file, just copy & paste it you will get a flag.txt file

Flag

BOH21{4lw4y5_u53_57r0nk_p455w0rd_;)_912429}

The Locker

Challenge files

An ELF file also, open it with Ghidra

The main function:

int main(void)

{
  int iVar1;
  long lVar2;
  size_t sVar3;
  ulong uVar4;
  long in_FS_OFFSET;
  int input;
  int i;
  char input2 [32];
  char answer [32];
  char key2 [32];
  char mainMenu1 [64];
  char mainMenu2 [64];
  char mainMenu3 [64];
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  mainMenu1._0_8_ = 0x656d6f636c65570a;
  mainMenu1._8_8_ = 0x20656874206f7420;
  mainMenu1._16_8_ = 0x212172656b636f4c;
  mainMenu1._24_8_ = 0xa21;
  mainMenu1._32_8_ = 0;
  mainMenu1._40_8_ = 0;
  mainMenu1._48_8_ = 0;
  mainMenu1._56_8_ = 0;
  mainMenu2._0_8_ = 0x6d2065766947207e;
  mainMenu2._8_8_ = 0x626d756e20612065;
  mainMenu2._16_8_ = 0x203a7265;
  mainMenu2._24_8_ = 0;
  mainMenu2._32_8_ = 0;
  mainMenu2._40_8_ = 0;
  mainMenu2._48_8_ = 0;
  mainMenu2._56_8_ = 0;
  mainMenu3._0_8_ = 0x6d2065766947207e;
  mainMenu3._8_8_ = 0x6972747320612065;
  mainMenu3._16_8_ = 0x203a676e;
  mainMenu3._24_8_ = 0;
  mainMenu3._32_8_ = 0;
  mainMenu3._40_8_ = 0;
  mainMenu3._48_8_ = 0;
  mainMenu3._56_8_ = 0;
  key2._0_8_ = 0x6a567933304a4d40;
  key2._8_8_ = 0x485d7b7436465d31;
  key2._16_8_ = 0x61324e5d71316c32;
  key2._24_8_ = 0x7f703169;
  lVar2 = ptrace(PTRACE_TRACEME,0,1,0);
  if (lVar2 < 0) {
    puts("Third Party Application has been detected!!!");
    iVar1 = 0;
  }
  else {
    puts(
         "       ...,NDDDDDN,...\n       .IDDDDDDDDDD87.\n    ...NDDDD=...,NDDDD..\n   ...ZDDD...    ..DDDD..\n   ...DDD$. ........DDD,.\n   ...DDD,..     ...DDD~..\n   ..,DDD...     ...DDD+..\n    .,DDD,...   .. .DDDI...\n...:=?DDDI??????????DDDZ~:.....\n...DDDDDDDDDDDD8DDDDDDDDDDDN...\n...DDDDN....$D.?DDZ....NDDDN...\n...DDDDN.ON.$D...DZ.N8.NDDDN...\n...DDDDN....$DDI.N$....NDDDN...\n...DDD8DDDDDD..DDDDD$$DDDDDN...\n...DDDDN...ID...ND=~..~DDDDN...\n...DDDDN.O. ...DDD,,,,.NDDDN...\n...DDDDDDDDDDDDI.D8IND?DDDDN...\n...DDDDN... $D...DZ.NDDDDDDN...\n...DDDDN.ON $DDDDDO~N8.NDDDN...\n ..DDDDN....$D.?D..?D..NDDDN.. \n  .NDDDDDDDDDDDDDDDDDDDDDDDN.  "
         );
    printf("%s",mainMenu1);
    puts("~ Give me the right input and I shall give you what you wanted...");
    printf("%s",mainMenu2);
    while( true ) {
      iVar1 = __isoc99_scanf(&DAT_00102316,&input);
      if (iVar1 == 1) break;
      puts("\nYou did not enter a valid number");
      printf("%s",mainMenu2);
      __isoc99_scanf(&DAT_00102312);
    }
    printf("%s",mainMenu3);
    __isoc99_scanf(&DAT_0010231a,input2);
    strcpy(answer,input2);
    i = 0;
    while( true ) {
      uVar4 = SEXT48(i);
      sVar3 = strlen(input2);
      if (sVar3 - 1 < uVar4) break;
      input2[i] = (char)input - 0x37U ^ input2[i];
      if (input2[i] != key2[i]) {
         puts("\n~ Looks like you are not the right person");
         iVar1 = -1;
         goto LAB_00101568;
      }
      i = i + 1;
    }
    puts("\n~ Well done, perhaps the wisdom have spoken to yourself");
    printf("%s",answer);
    iVar1 = 0;
  }
LAB_00101568:
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                      /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return iVar1;
}

We focus on the comparision part:

iVar1 = __isoc99_scanf(&DAT_00102316,&input);
if (iVar1 == 1) break;
puts("\nYou did not enter a valid number");
...
...
while( true ) {
  uVar4 = SEXT48(i);
  sVar3 = strlen(input2);
  if (sVar3 - 1 < uVar4) break;
  input2[i] = (char)input - 0x37U ^ input2[i];
  if (input2[i] != key2[i]) {
     puts("\n~ Looks like you are not the right person");
     iVar1 = -1;
     goto LAB_00101568;
  }
  i = i + 1;
}
puts("\n~ Well done, perhaps the wisdom have spoken to yourself");
printf("%s",answer);
iVar1 = 0;

As you can see it just XOR with a random number, then check is it equal to key2!

Wrote down the equation is like this:

key2 = (number - 0x37) ^ flag
flag = key2 ^ (number - 0x37)

We know the flag format start with BOH21, therefore we can calculate the XOR key and decrypt the flag:

from pwn import *
# Copy the value from Ghidra
key2 = p64(0x6a567933304a4d40)
key2 += p64(0x485d7b7436465d31)
key2 += p64(0x61324e5d71316c32)
key2 += p64(0x7f703169)
# B = key2[0] ^ (number - 0x37)
xor_key = key2[0] ^ ord('B')

for k in key2:
  print(chr(k^xor_key),end='')
# BOH21{Th3_D4vy_J0n3s_L0ck3r}

Flag

BOH21{Th3_D4vy_J0n3s_L0ck3r}

Not So Difficult

Challenge files

Also an ELF file, running strings on it we can see it contains some python libraries:

strings NotSoDifficult
...
...
...
blib-dynload/binascii.cpython-38-x86_64-linux-gnu.so
blib-dynload/grp.cpython-38-x86_64-linux-gnu.so
blib-dynload/math.cpython-38-x86_64-linux-gnu.so
blib-dynload/mmap.cpython-38-x86_64-linux-gnu.so
blib-dynload/pyexpat.cpython-38-x86_64-linux-gnu.so
blib-dynload/readline.cpython-38-x86_64-linux-gnu.so
blib-dynload/resource.cpython-38-x86_64-linux-gnu.so
blib-dynload/select.cpython-38-x86_64-linux-gnu.so
blib-dynload/termios.cpython-38-x86_64-linux-gnu.so
blib-dynload/unicodedata.cpython-38-x86_64-linux-gnu.so
blib-dynload/zlib.cpython-38-x86_64-linux-gnu.so
...
...
...

This is a indicator that this executable is produced by Pyinstaller

We need to “Uninstall” it back to python file, to do this just need pyinstxtractor.py

Run it with python3 will extract back the python file:

python3 pyinstxtractor.py NotSoDifficult
[+] Processing NotSoDifficult
[+] Pyinstaller version: 2.1+
[+] Python version: 38
[+] Length of package: 9386872 bytes
[+] Found 66 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: BOHChall3.pyc
[+] Found 223 files in PYZ archive
[+] Successfully extracted pyinstaller archive: NotSoDifficult

You can now use a python decompiler on the pyc files within the extracted directory

Look at the extracted folder, we can see there is a BOHChall3.pyc file

ls NotSoDifficult_extracted
BOHChall3.pyc         lib-dynload       libpython3.8.so   libz.so.1                    pyiboot01_bootstrap.pyc  pyimod04_ctypes.pyc
PYZ-00.pyz            libcrypto.so.1.1  libreadline.so.8  pyi_rth_inspect.pyc          pyimod01_os_path.pyc     struct.pyc
PYZ-00.pyz_extracted  libffi.so.7       libssl.so.1.1     pyi_rth_multiprocessing.pyc  pyimod02_archive.pyc
base_library.zip      liblzma.so.5      libtinfow.so.6    pyi_rth_pkgutil.pyc          pyimod03_importers.pyc

That is compiled python code, can decompile it using uncompyle6

# uncompyle6 BOHChall3.pyc

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 3.8.5 (default, Jan 27 2021, 15:41:15)
# [GCC 9.3.0]
# Embedded file name: BOHChall3.py
import base64, string

def convert(input, index):
    b = input.encode('UTF-8')
    if index == 0:
        e = base64.b16encode(b)
        d = e.decode('UTF-8')
        if d != '31543173':
            return 0
    if index == 4:
        e = base64.b32encode(b)
        d = e.decode('UTF-8')
        if d != 'KMYW24A=':
            return 0
    if index == 8:
        e = base64.b64encode(b)
        d = e.decode('UTF-8')
        if d != 'bDM0cw==':
            return 0
    elif index == 12:
        e = base64.a85encode(b)
        d = e.decode('UTF-8')
        if d != '<+n+1':
            return 0
    else:
        return 1


def checkInput(input):
    x = 0
    j = 0
    for i in range(x, len(input), 4):
        x = i
        if convert(input[x:x + 4], x) == 0:
            return 0
        return 1


usrInput = input('Enter password for authentication: ')
i = 0
if usrInput:
    if len(usrInput) == 16:
        if checkInput(usrInput) == 0:
            print('Wrong Password')
    else:
        print('Correct, the flag is the password')
else:
    print('Wrong Password')
# okay decompiling BOHChall3.pyc

As you can see, it is just base encoding, wrote a simple python script to decode it:

import base64
flag = base64.b16decode('31543173')
flag += base64.b32decode('KMYW24A=')
flag += base64.b64decode('bDM0cw==')
flag += base64.a85decode('<+n+1')
print(flag)
# 1T1sS1mpl34sTh4t

Flag

BOH21{1T1sS1mpl34sTh4t}

Summary

I think it is a good CTF for beginners, but notice some challenges with higher points but is easier than the lower points one. Also some challenges are similar just different title, therefore I think can improve the quality of the challenges and maybe all challenges put same points and decay by solves will be more fair for players.