Year3000

Year3000 [RE - Nullcon Hackim2020 CTF]

Description


One day when I came home at lunchtime I heard a funny noise Went out to the back yard to find out If it was one of those rowdy boys Stood there was my neighbor called Peter And a flux capacitor I guess there must be quite some entropy in a flux capacitor...

Netcat Link : nc re.ctf.nullcon.net 1234


we were given 3000 stripped ELF binaries some 64bit binaries and 32bit binaries. the description of the challenge did’nt explained what exactly the task was, so i tried to connect to the given netcat link.

x3ero0 :: year3000 » nc re.ctf.nullcon.net 1234
1252.bin
>

so it was clear that for each binary we need to send the correct password of the binary. lets reverse engineer one of the binaries.

public start
start proc near
xor     ebp, ebp
pop     esi
mov     ecx, esp
and     esp, 0FFFFFFF0h
push    eax
push    esp             ; stack_end
push    edx             ; rtld_fini
call    sub_542
add     ebx, 1AA0h
lea     eax, (nullsub_1 - 1FC0h)[ebx]
push    eax             ; fini
lea     eax, (sub_7B0 - 1FC0h)[ebx]
push    eax             ; init
push    ecx             ; ubp_av
push    esi             ; argc
push    ds:(off_1FF8 - 1FC0h)[ebx] ; main
call    ___libc_start_main
hlt
start endp

i calculated the offset of main function through gdb and it was sub_6D0

.text:000006D0
.text:000006D0 ; =============== S U B R O U T I N E =======================================
.text:000006D0
.text:000006D0 ; Attributes: bp-based frame fuzzy-sp
.text:000006D0
.text:000006D0 sub_6D0         proc near               ; DATA XREF: .got:off_1FF8↓o
.text:000006D0
.text:000006D0 var_74          = dword ptr -74h
.text:000006D0 s               = byte ptr -70h
.text:000006D0 var_C           = dword ptr -0Ch
.text:000006D0
.text:000006D0 ; __unwind {
.text:000006D0                 lea     ecx, [esp+4]
.text:000006D4                 and     esp, 0FFFFFFF0h
.text:000006D7                 push    dword ptr [ecx-4]
.text:000006DA                 push    ebp
.text:000006DB                 mov     ebp, esp
.text:000006DD                 push    ebx
.text:000006DE                 push    ecx
.text:000006DF                 sub     esp, 70h
.text:000006E2                 call    sub_550
.text:000006E7                 add     ebx, 18D9h
.text:000006ED                 mov     eax, large gs:14h
.text:000006F3                 mov     [ebp+var_C], eax
.text:000006F6                 xor     eax, eax
.text:000006F8                 mov     eax, ds:(stdin_ptr - 1FC0h)[ebx]
.text:000006FE                 mov     eax, [eax]
.text:00000700                 push    0               ; n
.text:00000702                 push    2               ; modes
.text:00000704                 push    0               ; buf
.text:00000706                 push    eax             ; stream
.text:00000707                 call    _setvbuf
.text:0000070C                 add     esp, 10h
.text:0000070F                 mov     eax, ds:(stdout_ptr - 1FC0h)[ebx]
.text:00000715                 mov     eax, [eax]
.text:00000717                 push    0               ; n
.text:00000719                 push    2               ; modes
.text:0000071B                 push    0               ; buf
.text:0000071D                 push    eax             ; stream
.text:0000071E                 call    _setvbuf
.text:00000723                 add     esp, 10h
.text:00000726                 mov     eax, ds:(stdin_ptr - 1FC0h)[ebx]
.text:0000072C                 mov     eax, [eax]
.text:0000072E                 sub     esp, 4
.text:00000731                 push    eax             ; stream
.text:00000732                 push    64h ; 'd'       ; n
.text:00000734                 lea     eax, [ebp+s]
.text:00000737                 push    eax             ; s
.text:00000738                 call    _fgets
.text:0000073D                 add     esp, 10h
.text:00000740                 sub     esp, 0Ch
.text:00000743                 lea     eax, [ebp+s]
.text:00000746                 push    eax
.text:00000747                 call    sub_64D
.text:0000074C                 add     esp, 10h
.text:0000074F                 mov     [ebp+var_74], eax
.text:00000752                 cmp     [ebp+var_74], 0
.text:00000756                 jz      short loc_76C
.text:00000758                 sub     esp, 0Ch
.text:0000075B                 lea     eax, (aWellDone - 1FC0h)[ebx] ; "Well done"
.text:00000761                 push    eax             ; s
.text:00000762                 call    _puts
.text:00000767                 add     esp, 10h
.text:0000076A                 jmp     short loc_77E
.text:0000076C ; ---------------------------------------------------------------------------
.text:0000076C
.text:0000076C loc_76C:                                ; CODE XREF: sub_6D0+86↑j
.text:0000076C                 sub     esp, 0Ch
.text:0000076F                 lea     eax, (aYouHaveFailed - 1FC0h)[ebx] ; "You have failed"
.text:00000775                 push    eax             ; s
.text:00000776                 call    _puts
.text:0000077B                 add     esp, 10h
.text:0000077E
.text:0000077E loc_77E:                                ; CODE XREF: sub_6D0+9A↑j
.text:0000077E                 cmp     [ebp+var_74], 1
.text:00000782                 setnz   al
.text:00000785                 movzx   eax, al
.text:00000788                 mov     edx, [ebp+var_C]
.text:0000078B                 xor     edx, large gs:14h
.text:00000792                 jz      short loc_799
.text:00000794                 call    sub_820
.text:00000799 ; ---------------------------------------------------------------------------
.text:00000799
.text:00000799 loc_799:                                ; CODE XREF: sub_6D0+C2↑j
.text:00000799                 lea     esp, [ebp-8]
.text:0000079C                 pop     ecx
.text:0000079D                 pop     ebx
.text:0000079E                 pop     ebp
.text:0000079F                 lea     esp, [ecx-4]
.text:000007A2                 retn
.text:000007A2 ; } // starts at 6D0
.text:000007A2 sub_6D0         endp
.text:000007A2

lets look at sub_64D

.text:0000064D ; =============== S U B R O U T I N E =======================================
.text:0000064D
.text:0000064D ; Attributes: bp-based frame
.text:0000064D
.text:0000064D sub_64D         proc near               ; CODE XREF: sub_6D0+77↓p
.text:0000064D
.text:0000064D var_15          = byte ptr -15h
.text:0000064D var_14          = dword ptr -14h
.text:0000064D var_10          = dword ptr -10h
.text:0000064D var_C           = dword ptr -0Ch
.text:0000064D var_4           = dword ptr -4
.text:0000064D arg_0           = dword ptr  8
.text:0000064D
.text:0000064D ; __unwind {
.text:0000064D                 push    ebp
.text:0000064E                 mov     ebp, esp
.text:00000650                 push    ebx
.text:00000651                 sub     esp, 00000014h
.text:00000654                 call    sub_7A3
.text:00000659                 add     eax, 1967h
.text:0000065E                 mov     [ebp+var_C], 54h ; 'T'
.text:00000665                 mov     [ebp+var_15], 63h ; 'c'
.text:00000669                 mov     [ebp+var_14], 1
.text:00000670                 mov     [ebp+var_10], 0
.text:00000677                 jmp     short loc_696
.text:00000679 ; ---------------------------------------------------------------------------
.text:00000679
.text:00000679 loc_679:                                ; CODE XREF: sub_64D+4F↓j
.text:00000679                 mov     ecx, [ebp+var_10]
.text:0000067C                 mov     edx, [ebp+arg_0]
.text:0000067F                 add     edx, ecx
.text:00000681                 movzx   edx, byte ptr [edx]
.text:00000684                 cmp     [ebp+var_15], dl
.text:00000687                 jz      short loc_692
.text:00000689                 mov     [ebp+var_14], 0
.text:00000690                 jmp     short loc_69E
.text:00000692 ; ---------------------------------------------------------------------------
.text:00000692
.text:00000692 loc_692:                                ; CODE XREF: sub_64D+3A↑j
.text:00000692                 add     [ebp+var_10], 1
.text:00000696
.text:00000696 loc_696:                                ; CODE XREF: sub_64D+2A↑j
.text:00000696                 mov     edx, [ebp+var_10]
.text:00000699                 cmp     edx, [ebp+var_C]
.text:0000069C                 jl      short loc_679
.text:0000069E
.text:0000069E loc_69E:                                ; CODE XREF: sub_64D+43↑j
.text:0000069E                 mov     ecx, [ebp+var_C]
.text:000006A1                 mov     edx, [ebp+arg_0]
.text:000006A4                 add     ecx, edx
.text:000006A6                 sub     esp, 4
.text:000006A9                 push    4               ; n
.text:000006AB                 lea     edx, (dword_2008 - 1FC0h)[eax]
.text:000006B1                 push    edx             ; s2
.text:000006B2                 push    ecx             ; s1
.text:000006B3                 mov     ebx, eax
.text:000006B5                 call    _memcmp
.text:000006BA                 add     esp, 10h
.text:000006BD                 test    eax, eax
.text:000006BF                 jz      short loc_6C8
.text:000006C1                 mov     [ebp+var_14], 0
.text:000006C8
.text:000006C8 loc_6C8:                                ; CODE XREF: sub_64D+72↑j
.text:000006C8                 mov     eax, [ebp+var_14]
.text:000006CB                 mov     ebx, [ebp+var_4]
.text:000006CE                 leave
.text:000006CF                 retn
.text:000006CF ; } // starts at 64D
.text:000006CF sub_64D         endp
.text:000006CF

if you see this function and notice that at 0x0000065e a number is loaded into dword [local_ch] and at 0x00000665 a character is loaded into byte [local_15h].

.text:0000065E                 mov     [ebp+var_C], 54h ; 'T'
.text:00000665                 mov     [ebp+var_15], 63h ; 'c'

the following loop after this simply checks if the user input is ‘c’ *0x54 then comes a memcmp at 0x6b5. now to statically analyse the arguments passed to memcmp we need to figure out whats going at 0x6b0

.text:000006AB                 lea     edx, (unk_2008 - 1FC0h)[eax]

now lets see what exactly is the value of eax at this instruction. we scroll a little bit up

.text:00000654                 call    sub_7A3
.text:00000659                 add     eax, 1967h

lets see what this function is at 0x7a3

.text:000007A3 sub_7A3         proc near               ; CODE XREF: sub_64D+7↑p
.text:000007A3 ; __unwind {
.text:000007A3                 mov     eax, [esp+0]
.text:000007A6                 retn
.text:000007A6 ; } // starts at 7A3
.text:000007A6 sub_7A3         endp
.text:000007A6

it returns the address which was pushed on the stack when this function was called. basically eax = address_of_next_instruction_after_call_0x7a3, so eax = 0x00000659 and then eax += 0x1967, so eax = 0x1fc0. okay now lets look at the instruction at 0x000006ab again

; from radare2
0x000006ab     lea     edx, dword [eax + 0x48] 

so it becomes

; from radare2
0x000006ab     lea     edx, dword [0x1fc0 + 0x48] ; 0x2008

and you can actually see that ida already calculated the offset for us. so memcmp checks that the bytes after ‘c’ * 0x54 with the dword value at 0x2008 which is

.data:00002008 dword_2008      dd 0A7C6D169h
Dump Of 3.bin

also one more thing to point out is that this value is always stored at the end of .data section. so the correct password for 3.bin is

import struct
print c * 0x54 + struct.pack(<I, 0xA7C6D169)

output ->

x3ero0 :: year3000 » python solve_3.py | ./3.bin
Well done

so i quickly wrote a script to fetch these values and generate a correct password for all the binaries both 64 bit binaries or 32 bit binaries.

exploit script –>

from pwn import *
import base64


context.log_level = "critical"

def solve(filename):

	file = open(filename, "rb").read()
	
	ELF64_Coun = 0x816
	ELF64_Valu = 0x81d

	ELF32_Coun = 0x65e
	ELF32_Valu = 0x665


	ELF64_Addr = 0x1010
	ELF32_Addr = 0x1008

	
	Format = ord(file[0x04])
	if (Format == 2):

		context.update(arch='amd64', os='linux')

		code_count = file[ELF64_Coun : ELF64_Coun+7]
		code_value = file[ELF64_Valu : ELF64_Valu+4]

		disa_count = disasm(code_count)
		disa_value = disasm(code_value)

		code_count = int(disa_count.split('0x')[-1], 16)
		code_value = chr(int(disa_value.split('0x')[-1], 16))

		#value = u64(file[ELF64_Addr : ELF64_Addr+8])

		value = file[ELF64_Addr : ELF64_Addr+8]
		value_n = u64(value)

		flag = code_value * code_count + value

	elif(Format == 1):

		context.update(arch='i386', os='linux')

		code_count = file[ELF32_Coun : ELF32_Coun+7]
		code_value = file[ELF32_Valu : ELF32_Valu+4]

		disa_count = disasm(code_count)
		disa_value = disasm(code_value)

		code_count = int(disa_count.split('0x')[-1], 16)
		code_value = chr(int(disa_value.split('0x')[-1], 16))

		#value = u32(file[ELF32_Addr:ELF32_Addr+4])

		value = file[ELF32_Addr:ELF32_Addr+4]
		value_n = u32(value)
		flag = code_value * code_count + value
	
	#print "[!] file : " + filename

	#print "[!] count: " + hex(code_count)

	#print "[!] value: " + code_value

	#print "[!] numbe: " + hex(value_n)


	return flag


def main():


	dump = ""
	i = 0
	p = remote("re.ctf.nullcon.net", 1234)
	a = ""
	temp = ""
	while('hackim' not in dump):

		# i noticed that only 10 files were asked

		if(i==10):
			print "Flag: " + p.recvline()
			p.close()
			exit()
		file = p.recvline().strip()

		a = p.recvuntil("> ")
		solver = solve(file)
		# .encode('base64') doesnt work with bytes 

		p.sendline(base64.b64encode(solver))
		#print str(solver).encode('base64')

		temp = p.recvline()
		if("Well done" in temp):
			print "[0x%.4x]\t" % i+ file + '\t: Well Done'
		else:
			print "[+] Failed:\t" + file + "\tdumping recieved data"
			exit()
		i += 1

if __name__ == "__main__":
	main()



and running this script gives us the flag