redpwnCTF - Bubbly

I was given a reverse engineering tutorial by teammate reloc for this challenge. I did not keep my specific notes during the tutorial, as I was trying to follow along and learn what was going on. I kept the challenge file and after waiting two weeks after the CTF ended, let’s see what I can remember about using gdb. The following differs a lot from what we did that first night we solved this. Reloc rewrote a portion of the program in python as we went along so that he could show me what was going on with the assembly code. I am not that good at coding and will be using a simpler method of setting a breakpoint in a for loop to examine what changes are happening as we go along.

First, let’s run the program to see what we can find out.

(gdb) run
Starting program: /root/Downloads/bubbly
I hate my data structures class! Why can't I just sort by hand?
7
3
4
9
Try again!
[Inferior 1 (process 2577) exited normally]

The program is taking a numeric input between 0 and 8, and anything higher exits the function. Let’s set the syntax to intel and disassemble the main function.

(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
   0x00005555555551d9 <+0>: 	push   rbp
   0x00005555555551da <+1>: 	mov	rbp,rsp
   0x00005555555551dd <+4>: 	sub	rsp,0x10
   0x00005555555551e1 <+8>: 	mov	rax,QWORD PTR [rip+0x2eb8]    	# 0x5555555580a0 
   0x00005555555551e8 <+15>:	mov	esi,0x0
   0x00005555555551ed <+20>:	mov	rdi,rax
   0x00005555555551f0 <+23>:	call   0x555555555040 
   0x00005555555551f5 <+28>:	mov	rax,QWORD PTR [rip+0x2eb4]    	# 0x5555555580b0 
   0x00005555555551fc <+35>:	mov	esi,0x0
   0x0000555555555201 <+40>:	mov	rdi,rax
   0x0000555555555204 <+43>:	call   0x555555555040 
   0x0000555555555209 <+48>:	mov	rax,QWORD PTR [rip+0x2eb0]    	# 0x5555555580c0 
   0x0000555555555210 <+55>:	mov	esi,0x0
   0x0000555555555215 <+60>:	mov	rdi,rax
   0x0000555555555218 <+63>:	call   0x555555555040 
   0x000055555555521d <+68>:	lea	rdi,[rip+0xdf4]    	# 0x555555556018
   0x0000555555555224 <+75>:	call   0x555555555030 
   0x0000555555555229 <+80>:	mov	BYTE PTR [rbp-0x1],0x0
=> 0x000055555555522d <+84>:	lea	rax,[rbp-0xc]
   0x0000555555555231 <+88>:	mov	rsi,rax
   0x0000555555555234 <+91>:	lea	rdi,[rip+0xe1d]    	# 0x555555556058
   0x000055555555523b <+98>:	mov	eax,0x0
   0x0000555555555240 <+103>:   call   0x555555555060 <__isoc99_scanf@plt>
   0x0000555555555245 <+108>:   mov	DWORD PTR [rbp-0x8],eax
   0x0000555555555248 <+111>:   mov	eax,DWORD PTR [rbp-0xc]
   0x000055555555524b <+114>:   cmp	eax,0x8
   0x000055555555524e <+117>:   ja 	0x55555555534d 
   0x0000555555555254 <+123>:   mov	eax,DWORD PTR [rbp-0xc]
   0x0000555555555257 <+126>:   mov	eax,eax
   0x0000555555555259 <+128>:   lea	rdx,[rax*4+0x0]
   0x0000555555555261 <+136>:   lea	rax,[rip+0x2df8]    	# 0x555555558060 
   0x0000555555555268 <+143>:   mov	edx,DWORD PTR [rdx+rax*1]
   0x000055555555526b <+146>:   mov	eax,DWORD PTR [rbp-0xc]
   0x000055555555526e <+149>:   add	eax,0x1
   0x0000555555555271 <+152>:   mov	eax,eax
   0x0000555555555273 <+154>:   lea	rcx,[rax*4+0x0]
   0x000055555555527b <+162>:   lea	rax,[rip+0x2dde]    	# 0x555555558060 
   0x0000555555555282 <+169>:   mov	eax,DWORD PTR [rcx+rax*1]
   0x0000555555555285 <+172>:   mov	esi,DWORD PTR [rbp-0xc]
   0x0000555555555288 <+175>:   mov	ecx,edx
   0x000055555555528a <+177>:   xor	ecx,eax
   0x000055555555528c <+179>:   mov	eax,esi
   0x000055555555528e <+181>:   lea	rdx,[rax*4+0x0]
   0x0000555555555296 <+189>:   lea	rax,[rip+0x2dc3]    	# 0x555555558060 
   0x000055555555529d <+196>:   mov	DWORD PTR [rdx+rax*1],ecx
   0x00005555555552a0 <+199>:   mov	eax,DWORD PTR [rbp-0xc]
   0x00005555555552a3 <+202>:   add	eax,0x1
   0x00005555555552a6 <+205>:   mov	eax,eax
   0x00005555555552a8 <+207>:   lea	rdx,[rax*4+0x0]
   0x00005555555552b0 <+215>:   lea	rax,[rip+0x2da9]    	# 0x555555558060 
   0x00005555555552b7 <+222>:   mov	edx,DWORD PTR [rdx+rax*1]
   0x00005555555552ba <+225>:   mov	eax,DWORD PTR [rbp-0xc]
   0x00005555555552bd <+228>:   mov	eax,eax
   0x00005555555552bf <+230>:   lea	rcx,[rax*4+0x0]
   0x00005555555552c7 <+238>:   lea	rax,[rip+0x2d92]    	# 0x555555558060 
   0x00005555555552ce <+245>:   mov	eax,DWORD PTR [rcx+rax*1]
   0x00005555555552d1 <+248>:   mov	ecx,DWORD PTR [rbp-0xc]
   0x00005555555552d4 <+251>:   lea	esi,[rcx+0x1]
   0x00005555555552d7 <+254>:   mov	ecx,edx
   0x00005555555552d9 <+256>:   xor	ecx,eax
   0x00005555555552db <+258>:   mov	eax,esi
   0x00005555555552dd <+260>:   lea	rdx,[rax*4+0x0]
   0x00005555555552e5 <+268>:   lea	rax,[rip+0x2d74]    	# 0x555555558060 
   0x00005555555552ec <+275>:   mov	DWORD PTR [rdx+rax*1],ecx
   0x00005555555552ef <+278>:   mov	eax,DWORD PTR [rbp-0xc]
   0x00005555555552f2 <+281>:   mov	eax,eax
   0x00005555555552f4 <+283>:   lea	rdx,[rax*4+0x0]
   0x00005555555552fc <+291>:   lea	rax,[rip+0x2d5d]    	# 0x555555558060 
   0x0000555555555303 <+298>:   mov	edx,DWORD PTR [rdx+rax*1]
   0x0000555555555306 <+301>:   mov	eax,DWORD PTR [rbp-0xc]
   0x0000555555555309 <+304>:   add	eax,0x1
   0x000055555555530c <+307>:   mov	eax,eax
   0x000055555555530e <+309>:   lea	rcx,[rax*4+0x0]
   0x0000555555555316 <+317>:   lea	rax,[rip+0x2d43]    	# 0x555555558060 
   0x000055555555531d <+324>:   mov	eax,DWORD PTR [rcx+rax*1]
   0x0000555555555320 <+327>:   mov	esi,DWORD PTR [rbp-0xc]
   0x0000555555555323 <+330>:   xor	edx,eax
   0x0000555555555325 <+332>:   mov	ecx,edx
   0x0000555555555327 <+334>:   mov	eax,esi
   0x0000555555555329 <+336>:   lea	rdx,[rax*4+0x0]
   0x0000555555555331 <+344>:   lea	rax,[rip+0x2d28]    	# 0x555555558060 
   0x0000555555555338 <+351>:   mov	DWORD PTR [rdx+rax*1],ecx
   0x000055555555533b <+354>:   mov	eax,0x0
   0x0000555555555340 <+359>:   call   0x555555555165 
   0x0000555555555345 <+364>:   mov	BYTE PTR [rbp-0x1],al
   0x0000555555555348 <+367>:   jmp	0x55555555522d 
   0x000055555555534d <+372>:   nop
   0x000055555555534e <+373>:   cmp	BYTE PTR [rbp-0x1],0x0
   0x0000555555555352 <+377>:   je 	0x55555555536c 
   0x0000555555555354 <+379>:   lea	rdi,[rip+0xd00]    	# 0x55555555605b
   0x000055555555535b <+386>:   call   0x555555555030 
   0x0000555555555360 <+391>:   mov	eax,0x0
   0x0000555555555365 <+396>:   call   0x5555555551bf 
   0x000055555555536a <+401>:   jmp	0x555555555378 
   0x000055555555536c <+403>:   lea	rdi,[rip+0xcf3]    	# 0x555555556066
   0x0000555555555373 <+410>:   call   0x555555555030 
   0x0000555555555378 <+415>:   mov	eax,0x0
   0x000055555555537d <+420>:   leave  
   0x000055555555537e <+421>:   ret    
End of assembler dump.

The part of the disassembled code that jumps out to me is at <+103>, where we see the scanf function listed. That is the area of the program that we are entering our values. The code below that takes our input and begins to pull values from an array and swap them around and put them back into the array. The program will loop through this function until we enter a number over 8. We can see the numbers in the array by using display nums.

(gdb) disp nums
1: nums = {1, 10, 3, 2, 5, 9, 8, 7, 4, 6}

If we set a breakpoint before we enter each value, we can see the changes to the array, and enter our next value accordingly.

(gdb) break 38
Breakpoint 3 at 0x55555555522d: file main.c, line 38.
(gdb) run
Starting program: /root/Downloads/bubbly
I hate my data structures class! Why can't I just sort by hand?

Breakpoint 3, main () at main.c:38
38  	in main.c
1: nums = {1, 10, 3, 2, 5, 9, 8, 7, 4, 6}
(gdb) c
Continuing.
1

Breakpoint 3, main () at main.c:38
38  	in main.c
1: nums = {1, 3, 10, 2, 5, 9, 8, 7, 4, 6}
(gdb) c
Continuing.
2

Breakpoint 3, main () at main.c:38
38  	in main.c
1: nums = {1, 3, 2, 10, 5, 9, 8, 7, 4, 6}
(gdb)

You can see after we begin running the program it will break before the input and display the nums array to us. We use “c” to continue and then enter our value. The array starts at value 0, so we can use the values 0-8 to swap all of the values in the array. From this point there are a lot of ways you can go about this, but the final goal is to have all the values in order. Entering a value over 8 enters the check to see if they are in order or not. If they are, the program will run the print_flag function and give us the flag.

Breakpoint 3, main () at main.c:38
38  	in main.c
1: nums = {1, 2, 3, 5, 4, 6, 7, 8, 9, 10}
(gdb) c
Continuing.
3

Breakpoint 3, main () at main.c:38
38  	in main.c
1: nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
(gdb) c
Continuing.
9
Well done!
[Detaching after vfork from child process 2681]
cat: flag.txt: No such file or directory
[Inferior 1 (process 2621) exited normally]

As long as we take note of the order that we used to solve it, all we have to do is netcat over to their server and enter the same string of digits. I used the following: 1 2 3 4 5 6 7 8 4 5 6 7 1 4 5 6 4 5 3

$ nc 2020.redpwnc.tf 31039
I hate my data structures class! Why can't I just sort by hand?
1
2
3
4
5
6
7
8
4
5
6
7
1
4
5
6
4
5
3
9
Well done!
Flag{4ft3r_y0u_put_u54c0_0n_y0ur_c011ege_4pp5_y0u_5t1ll_h4ve_t0_d0_th15_57uff}

This challenge was a fantastic introduction to reverse engineering and gdb. I cannot thank Reloc enough for his assistance and I can’t wait for my next opportunity to learn some more reverse engineering.