↓Skip to main content
  1. Posts/

Insomnihack Teaser 2022: Herald

Read more write-ups at kitctf.de.

Challenge description:

Our lab administrator has just passed out from a strange virus. Please help us find the password to his messaging app so we can identify what he was working on and save his life.

We are given an APK (Android Package). When we start it, it asks for a username and password. It does not require any network connection, so it is a classic crackme.

APKs are just ZIP archives, so we can get the contained files by just renaming it to Herald.zip and unzipping. Looking at the source, it becomes clear that this is a React Native application. The real source of a React Native application is located in assets/index.android.bundle. This used to be just minified JavaScript in a webpack bundle. With the file command, however, we can see that it is a modern app with Hermes bytecode. Judging by the challenge name (Herald, like Hermes, the herald of the gods), this is what the challenge is about.

index.android.bundle: Hermes JavaScript bytecode, version 84

There is hbctool that can disassemble Hermes bytecode. The code for version 76 works well for our purposes.

hbctool disasm index.android.bundle labasm

This gives us a 275284 line file of disassembly. Searching through it, we find the Function<tryAuth>4087(3 params, 13 registers, 0 symbols) function. It seems to call all the relevant functions and do the checks. We invert the marked checks (the end-of-line comments show the patched instructions) to get access.

Function<tryAuth>4087(3 params, 13 registers, 0 symbols):
	LoadThisNS          	Reg8:2
	GetByIdShort        	Reg8:0, Reg8:2, UInt8:1, UInt8:121
	; Oper[3]: String(121) 'state'

	GetById             	Reg8:0, Reg8:0, UInt8:2, UInt16:4142
	; Oper[3]: String(4142) 'username'

	LoadConstString     	Reg8:1, UInt16:801
	; Oper[1]: String(801) 'admin'

	JStrictNotEqual     	Addr8:38, Reg8:0, Reg8:1 ; > JStrictEqual Addr8:38, Reg8:0, Reg8:1
	GetByIdShort        	Reg8:0, Reg8:2, UInt8:1, UInt8:121
	; Oper[3]: String(121) 'state'

	GetById             	Reg8:3, Reg8:0, UInt8:3, UInt16:4120
	; Oper[3]: String(4120) 'password'

	GetById             	Reg8:4, Reg8:2, UInt8:4, UInt16:3485
	; Oper[3]: String(3485) 'decodedText'

	NewArrayWithBuffer  	Reg8:0, UInt16:28, UInt16:28, UInt16:9398
	Call2               	Reg8:0, Reg8:4, Reg8:2, Reg8:0
	JStrictEqual        	Addr8:105, Reg8:3, Reg8:0 ; > JStrictNotEqual Addr8:105, Reg8:3, Reg8:0
	GetByIdShort        	Reg8:0, Reg8:2, UInt8:1, UInt8:121
	; Oper[3]: String(121) 'state'

	GetById             	Reg8:0, Reg8:0, UInt8:2, UInt16:4142
	; Oper[3]: String(4142) 'username'

	JStrictEqual        	Addr8:45, Reg8:0, Reg8:1 ; > JStrictNotEqual Addr8:45, Reg8:0, Reg8:1
	GetEnvironment      	Reg8:0, UInt8:1
	LoadFromEnvironment 	Reg8:0, Reg8:0, UInt8:6
	GetById             	Reg8:3, Reg8:0, UInt8:5, UInt16:4460
	; Oper[3]: String(4460) 'Alert'

	GetById             	Reg8:0, Reg8:3, UInt8:6, UInt16:5067
	; Oper[3]: String(5067) 'alert'

	LoadConstString     	Reg8:1, UInt16:1772
	; Oper[1]: String(1772) 'Wrong Username/Password combination'

	Call2               	Reg8:0, Reg8:0, Reg8:3, Reg8:1
	GetById             	Reg8:0, Reg8:2, UInt8:7, UInt16:3904
	; Oper[3]: String(3904) 'setPrint'

	Call2               	Reg8:0, Reg8:0, Reg8:2, Reg8:1
	Jmp                 	Addr8:66
	GetEnvironment      	Reg8:0, UInt8:1
	LoadFromEnvironment 	Reg8:0, Reg8:0, UInt8:6
	GetById             	Reg8:3, Reg8:0, UInt8:5, UInt16:4460
	; Oper[3]: String(4460) 'Alert'

	GetById             	Reg8:1, Reg8:3, UInt8:6, UInt16:5067
	; Oper[3]: String(5067) 'alert'

	LoadConstString     	Reg8:0, UInt16:800
	; Oper[1]: String(800) 'You are not the admin, Liar!'

	Call2               	Reg8:0, Reg8:1, Reg8:3, Reg8:0
	GetById             	Reg8:1, Reg8:2, UInt8:7, UInt16:3904
	; Oper[3]: String(3904) 'setPrint'

	LoadConstString     	Reg8:0, UInt16:1312
	; Oper[1]: String(1312) 'Attack attempt detected'

	Call2               	Reg8:0, Reg8:1, Reg8:2, Reg8:0
	Jmp                 	Addr8:21
	GetById             	Reg8:1, Reg8:2, UInt8:8, UInt16:3839
	; Oper[3]: String(3839) 'decodedFlag'

	NewArrayWithBuffer  	Reg8:0, UInt16:43, UInt16:43, UInt16:9512
	Call2               	Reg8:0, Reg8:1, Reg8:2, Reg8:0
	GetEnvironment      	Reg8:0, UInt8:1
	LoadFromEnvironment 	Reg8:0, Reg8:0, UInt8:6
	GetById             	Reg8:1, Reg8:0, UInt8:9, UInt16:3819
	; Oper[3]: String(3819) 'Keyboard'

	GetById             	Reg8:0, Reg8:1, UInt8:10, UInt16:3822
	; Oper[3]: String(3822) 'dismiss'

	Call1               	Reg8:0, Reg8:0, Reg8:1
	LoadConstUndefined  	Reg8:0
	Ret                 	Reg8:0
EndFunction

We can reassemble it, repackage the apk and sign it.

hbctool asm labasm ./Herald/assets/index.android.bundle
apktool b Herald/
java -jar signapk.jar certificate.pem key.pk8 ./Herald/dist/Herald.apk signed.apk

Running the app and logging in with KITCTF, KITCTF gives us the flag.

INS{Y0u_Kn0W_aB0uT_Th3_Her4ld_0F_the_G0ds?}