0xd13a

A rookie in a world of pwns

ASIS CTF Quals 2018 Writeup: Density

Density

Keep it short and simple

This challenge contains an executable and an encoded data file generated by it. We need to reverse the encoding:

1
2
3
4
5
$ ./b64pack 
usage: ./befshar input

$ ./b64pack aaa
?????k??????n??

The logic can be quickly analyzed in Hex Rays or Snowman, or traced in the debugger. Once the parameter is supplied the following chunk of code does the processing of it (see comments inside):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...
.text:000055594AC08C3D loc_55594AC08C3D:                       ; CODE XREF: main+20?j
.text:000055594AC08C3D                 xor     eax, eax
.text:000055594AC08C3F                 call    sub_55594AC08DE0      ; Generate a random string composed of printable characters
.text:000055594AC08C44                 mov     r12, rax
.text:000055594AC08C47                 xor     eax, eax
.text:000055594AC08C49                 call    sub_55594AC08DE0      ; Generate another random string
.text:000055594AC08C4E                 mov     rsi, [rbp+8]    ; src
.text:000055594AC08C52                 mov     rbx, rax
.text:000055594AC08C55                 mov     rdi, r12        ; src
.text:000055594AC08C58                 call    sub_55594AC08F40      ; Append command line string to first random string
.text:000055594AC08C5D                 mov     rsi, rbx        ; src
.text:000055594AC08C60                 mov     rdi, rax        ; src
.text:000055594AC08C63                 call    sub_55594AC08F40      ; Append second random string to the result
.text:000055594AC08C68                 mov     rbx, rax
.text:000055594AC08C6B                 or      rcx, 0FFFFFFFFFFFFFFFFh
.text:000055594AC08C6F                 xor     eax, eax
.text:000055594AC08C71                 mov     rdi, rbx
.text:000055594AC08C74                 lea     rsi, [rsp+28h+var_20]
.text:000055594AC08C79                 repne scasb                   ; Find resulting string length
.text:000055594AC08C7B                 mov     rdi, rbx
.text:000055594AC08C7E                 mov     rax, rcx
.text:000055594AC08C81                 not     rax
.text:000055594AC08C84                 sub     rax, 1
.text:000055594AC08C88                 mov     [rsp+28h+var_20], rax
.text:000055594AC08C8D                 call    sub_55594AC08FB0      ; Encode the string
.text:000055594AC08C92                 mov     rsi, [rsp+28h+var_20]
.text:000055594AC08C97                 mov     rdi, rbx
.text:000055594AC08C9A                 call    sub_55594AC094F0      ; ...and print it
.text:000055594AC08C9F                 mov     edi, 0Ah        ; c
.text:000055594AC08CA4                 call    _putchar
.text:000055594AC08CA9                 jmp     loc_55594AC08C13
...

Encoding logic in sub_55594AC08FB0 is as follows:

  • Convert each character from set @$_!\"#%&'()*+,-./:;<=>?\n to + and chr(character position + ord('a'))
  • Convert each character from set [\\]^{|}~`\t to ++ and chr(character position + ord('a'))
  • For all characters convert each to its position in Base64 character set (expressed as a 6-bit value) and glue these bit strings together to produce the result

To recover our encoded string let’s do these operations in reverse order:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
b64str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
punct1 = "@$_!\"#%&'()*+,-./:;<=>?\n"
punct2 = "[\\]^{|}~`\t" 

data = bytearray(open('short','rb').read())

# convert to bitstring
bitstring = ''.join(['{:08b}'.format(x) for x in data])

# unpack every 6 bits into a Base64 characater
decoded = ''
for x in range(0,len(bitstring),6):
	decoded += b64str[int(bitstring[x:x+6],2)]

# decode special "+" encoding 
out = ''
i = 0
while i < len(decoded):
	if decoded[i] == '+':
		i += 1
		if decoded[i] == '+':
			i += 1
			out += punct2[ord(decoded[i])-ord('a')]
		else:
			out += punct1[ord(decoded[i])-ord('a')]
	else:
		out += decoded[i]
	i += 1
	
print out

Running the script produces the flag:

1
2
$ python solve.py 
O~$/cASIS{01d_4Nd_GoLD_ASIS_1De4_4H4t_g0e5_f0r_ls!}dI	)M>b|D9W//CC

The flag is ASIS{01d_4Nd_GoLD_ASIS_1De4_4H4t_g0e5_f0r_ls!}.