====== level6 ====== Hint: understand the algorithm for accessing the integer array. Let's open the binary with gdb. red9057@red9057-vm-ctf1 /home/labs/week1/level6 $ gdb ./level6 Reading symbols from ./level6...(no debugging symbols found)...done. gdb-peda$ disas password Dump of assembler code for function password: We will detect call, arguments, loop, branch, etc. sequentially. 0x08048570 <+0>: push %ebp <-- function prologue 0x08048571 <+1>: mov %esp,%ebp 0x08048573 <+3>: push %esi 0x08048574 <+4>: sub $0x54,%esp 0x08048577 <+7>: lea 0x80487e7,%eax <-- 3. argument came from here. 0x0804857d <+13>: mov %eax,(%esp) <-- 2. 1st argument 0x08048580 <+16>: call 0x8048370 <-- 1. call printf gdb-peda$ x/s 0x80487e7 0x80487e7: "Send me 7 lucky integers\n" Code: printf("Send me 7 lucky integers\n") 0x08048585 <+21>: movl $0x0,-0x8(%ebp) <-- 5. loop assignment 0x0804858c <+28>: mov %eax,-0x38(%ebp) 0x0804858f <+31>: cmpl $0x7,-0x8(%ebp) <-- 6. loop comparison 0x08048593 <+35>: jge 0x8048608 <-- 1. JUMP to 152 Read 1 (above),2,3, and 4 (below). Let's create the loop. Code: int i = ebp_8; // It's loop with i+=1 (because of 4.), so let's create 'for' for(i=0; i<7; ++i) { // jge, which is >=, and we negate that to < to not to jump. 0x08048599 <+41>: lea 0x8048801,%eax 0x0804859f <+47>: mov -0x8(%ebp),%ecx <-- 10. 2nd comes from here, ebp_8, i. 0x080485a2 <+50>: add $0x1,%ecx <-- 11. 2nd added by 1. 0x080485a5 <+53>: mov %eax,(%esp) <-- 9. 1st arg, 0x8048801 0x080485a8 <+56>: mov %ecx,0x4(%esp) <-- 8. 2nd arg 0x080485ac <+60>: call 0x8048370 <-- 7. call it could be printf(0x8048801, i+1), and Code: printf("Integer %d: ", i+1); 0x080485b1 <+65>: lea 0x804880e,%ecx <-- 15. 1st came from here 0x080485b7 <+71>: lea -0x34(%ebp),%edx <-- 18. related to 2nd, seems like a 'buffer' at ebp_34 (reasoning: lea -> address -> a buffer) 0x080485ba <+74>: mov -0x8(%ebp),%esi <-- 16. origin of 2nd, i. 0x080485bd <+77>: shl $0x2,%esi <-- 17. esi = i << 2, which is i*4. 0x080485c0 <+80>: add %esi,%edx <-- 19. ebp_34 + i * 4... 0x080485c2 <+82>: mov %ecx,(%esp) <-- 14. 1st arg 0x080485c5 <+85>: mov %edx,0x4(%esp) <-- 13. 2nd arg 0x080485c9 <+89>: mov %eax,-0x3c(%ebp) 0x080485cc <+92>: call 0x80483c0 <__isoc99_scanf@plt> <-- 12. another call. gdb-peda$ x/s 0x804880e 0x804880e: "%d" Code: scanf("%d", buffer[i*4]); //ebp_34 is buffer. The index is multiplied by 4 (seems a 32bit int), so -> int buffer[]; scanf("%d", buffer[i]); // this will be a better representation. 0x080485d1 <+97>: lea 0x8048811,%ecx <-- 24. 1st is here. 0x080485d7 <+103>: mov -0x8(%ebp),%edx <-- 25. edx = i; 0x080485da <+106>: add $0x1,%edx <-- 26. edx = i+1; 0x080485dd <+109>: mov -0x8(%ebp),%esi <-- 27. esi = i 0x080485e0 <+112>: mov -0x34(%ebp,%esi,4),%esi <-- 28. esi = ebp_34[esi*4], esi = buffer[i*4] (based on 27) 0x080485e4 <+116>: mov %ecx,(%esp) <-- 23. 1st arg, ecx 0x080485e7 <+119>: mov %edx,0x4(%esp) <-- 22. 2nd arg, edx 0x080485eb <+123>: mov %esi,0x8(%esp) <-- 21. 3rd arg, esi 0x080485ef <+127>: mov %eax,-0x40(%ebp) 0x080485f2 <+130>: call 0x8048370 <-- 20. another call gdb-peda$ x/s 0x8048811 0x8048811: "Integer %d is %d\n" Code: printf("Integer %d is %d\n", i+1, buffer[i]); 0x080485f7 <+135>: mov %eax,-0x44(%ebp) 0x080485fa <+138>: mov -0x8(%ebp),%eax <-- 3. seems ebp_8 is the loop variable. 0x080485fd <+141>: add $0x1,%eax <-- 4. it does i+=1; 0x08048600 <+144>: mov %eax,-0x8(%ebp) 0x08048603 <+147>: jmp 0x804858f <-- 2. before JUMP target, there is jump-back; it's a LOOP! 0x08048608 <+152>: mov -0x8(%ebp),%eax <-- JUMP TARGET >>> COLLECT CODE FOR LOOP printf("Send me 7 lucky integers\n") int i = ebp_8; // It's loop with i+=1 (because of 4.), so let's create 'for' int buffer[]; // at ebp_34 for(i=0; i<7; i++) { // jge, which is >=, and we negate that to < to not to jump. printf("Integer %d: ", i+1); scanf("%d", buffer[i]); // this will be a better representation. printf("Integer %d is %d\n", i+1, buffer[i]); } <<< Let's go for the next. 0x08048608 <+152>: mov -0x8(%ebp),%eax <-- 4. eax = previous i, which is 7 (the end condition of the loop 1) 0x0804860b <+155>: mov 0x804a02c(,%eax,4),%eax <-- 5. eax = mem_804a02c[eax*4]; eax = mem_804a02c[7] 0x08048612 <+162>: mov %eax,-0xc(%ebp) <-- 6. ebp_c = mem_804a02c[7]; 0x08048615 <+165>: movl $0x0,-0x8(%ebp) <-- 7. i = 0; 0x0804861c <+172>: cmpl $0x7,-0x8(%ebp) <-- 8. i (jge, so negate), i<7; 0x08048620 <+176>: jge 0x804869d <-- 1. JUMP to +301 Based on 1-8, let's create a for loop. Code: mem_804a02c[7] = 7; ebp_c = 7; for(int i=0; i<7; i++) { 0x08048626 <+182>: movl $0x0,-0xc(%ebp) <-- 14. j = 0 0x0804862d <+189>: cmpl $0x6,-0xc(%ebp) <-- 15. j < 6 (jge, >=, negate, <)/ 0x08048631 <+193>: jge 0x804868a <-- 9. JUMP again? Let's create a 2nd for loop here. Code: for(int j=0 j<6; ++j) { 0x08048637 <+199>: mov -0xc(%ebp),%eax <-- 16. eax = j; 0x0804863a <+202>: mov 0x804a02c(,%eax,4),%eax <-- 17. eax = (int)mem_0x804a02c[j] 0x08048641 <+209>: mov %eax,-0x10(%ebp) <-- 18. ebp_10 = (int)mem_0x804a02c[j] 0x08048644 <+212>: mov -0xc(%ebp),%eax <-- 19. eax = j; 0x08048647 <+215>: mov 0x804a030(,%eax,4),%eax <-- 20. eax = (int)mem_0x804a030[j]; 0x0804864e <+222>: mov %eax,-0x14(%ebp) <-- 21. ebp_14 = (int)mem_0x804a030[j]; 0x08048651 <+225>: mov -0x10(%ebp),%eax <-- 22. eax = ebp_10; From 16-22, ebp_10 = (int)mem_0x804a02c[j]; ebp_14 = (int)mem_0x804a030[j]; These are memory access to (0x804a02c+j*4)and (0x804a030+j*4), which is (0x804a02c + j * 4), (0x804a02c + 4 + j * 4), which is equivalent to: 0x804a02c + j*4, 0x804a02c + (1+j) * 4, then it will be: ebp_10 = (int)mem_0x804a02c[j]; ebp_14 = (int)mem_0x804a02c[j+1]; Because we know that mem_0x804a02c is an array of integer, let's let this be an int array[] (it's a global variable). Code: ebp_10 = array[j]; ebp_14 = array[j+1]; An cmp here, jle to 263, 263 is just a jump out. 0x08048654 <+228>: cmp -0x14(%ebp),%eax 0x08048657 <+231>: jle 0x8048677 Code: if(ebp_10 > ebp_14) { 0x0804865d <+237>: mov -0x14(%ebp),%eax <-- 23. eax = ebp_14 0x08048660 <+240>: mov -0xc(%ebp),%ecx <-- 24. ecx = j 0x08048663 <+243>: mov %eax,0x804a02c(,%ecx,4) <-- 25. array[j] = eax = ebp_14; 0x0804866a <+250>: mov -0x10(%ebp),%eax <-- 26. eax = ebp_10 0x0804866d <+253>: mov -0xc(%ebp),%ecx <-- 27. ecx = j 0x08048670 <+256>: mov %eax,0x804a030(,%ecx,4) <-- 28. mem_0x804a030[j] = array[j+1] = eax = ebp_10 Let's interpret 23-28. From 23-25, array[j] = ebp_14. From 26-28, array[j+1] = ebp_10. These values were: ebp_10 = array[j]; ebp_14 = array[j+1]; So it seems these assembly does 'swap' 'j'th item with 'j+1'th item if the condition array[j] > array[j+1]. New Code: if(array[j] > array[j+1]) { int t = array[j]; \ array[j] = array[j+1]; |------> a swap(array[j], array[j+1]) operation array[j+1] = t; / } 0x08048677 <+263>: jmp 0x804867c 0x0804867c <+268>: mov -0xc(%ebp),%eax <-- 12. Looks like ebp_c is the nested loop variable, say it's 'j' 0x0804867f <+271>: add $0x1,%eax <-- 13. j += 1 for the for loop... 0x08048682 <+274>: mov %eax,-0xc(%ebp) 0x08048685 <+277>: jmp 0x804862d <-- 11. JUMP back 2? A nested LOOP. 0x0804868a <+282>: jmp 0x804868f <-- 10. JUMP TARGET 2 Loop2 ends here, 0x0804868f <+287>: mov -0x8(%ebp),%eax <-- 4. LOOP variable: ebp_8; 0x08048692 <+290>: add $0x1,%eax 0x08048695 <+293>: mov %eax,-0x8(%ebp) 0x08048698 <+296>: jmp 0x804861c <-- 3. JUMP back. A LOOP! 0x0804869d <+301>: movl $0x0,-0x8(%ebp) <-- 2. JUMP TARGET Loop1 ends here. Let's collect the code for these two loops. >>> COLLECTED CODE array[7] = 7; for(int i=0; i<7; i++) { for(int j=0 j<6; ++j) { if(array[j] > array[j+1]) { int t = array[j]; \ array[j] = array[j+1]; |------> a swap(array[j], array[j+1]) operation array[j+1] = t; / } } } <<< What does this function do? Yes, its bubble sort. Before moving to the next, let's check what kind of values were stored in the array, at 0x804a02c. We will use x/7x to print seven integer values. gdb-peda$ x/7x 0x804a02c 0x804a02c : 0x00000001 0x0000000d 0x00000005 0x00000002 0x804a03c : 0x00000001 0x00000003 0x00000008 It seems like the name of array is 'keys' (seems storing an important info), and its values are: 1, 0xd (13), 5, 2, 1, 3, 8 (base 10 number for the parenthesis), which are: 1, 13, 5, 2, 1, 3, 8. And the algorithm right before this was 'bubble sort', running on this array. Then, the values will be sorted. Let's check if our reverse engineering result is correct by running the program upto this point. It's right before password+301 (see <-- 1. below), so we will set a breakpoint at password+301 and run the program. === GDB START === gdb-peda$ r Starting program: /home/labs/week1/level6/level6 Let's start an esoteric game! Send me 7 lucky integers <-- I typed a random 7 numbers... Integer 1: 1 Integer 1 is 1 Integer 2: 2 Integer 2 is 2 Integer 3: 3 Integer 3 is 3 Integer 4: 4 Integer 4 is 4 Integer 5: 5 Integer 5 is 5 Integer 6: 6 Integer 6 is 6 Integer 7: 7 Integer 7 is 7 [----------------------------------registers-----------------------------------] EAX: 0x7 EBX: 0x0 ECX: 0x1 EDX: 0xf7fb5870 --> 0x0 ESI: 0x7 EDI: 0xf7fb4000 --> 0x1b1db0 EBP: 0xffffd648 --> 0xffffd658 --> 0x0 ESP: 0xffffd5f0 --> 0x8048811 ("Integer %d is %d\n") EIP: 0x804869d (: movl $0x0,-0x8(%ebp)) EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x8048692 : add $0x1,%eax 0x8048695 : mov %eax,-0x8(%ebp) 0x8048698 : jmp 0x804861c => 0x804869d : movl $0x0,-0x8(%ebp) 0x80486a4 : cmpl $0x7,-0x8(%ebp) 0x80486a8 : jge 0x80486ee 0x80486ae : mov -0x8(%ebp),%eax 0x80486b1 : mov 0x804a02c(,%eax,4),%eax [------------------------------------stack-------------------------------------] 0000| 0xffffd5f0 --> 0x8048811 ("Integer %d is %d\n") 0004| 0xffffd5f4 --> 0x7 0008| 0xffffd5f8 --> 0x7 0012| 0xffffd5fc --> 0x8f17 0016| 0xffffd600 --> 0xf7ffd000 --> 0x23f3c 0020| 0xffffd604 --> 0xf 0024| 0xffffd608 --> 0x1 0028| 0xffffd60c --> 0xb ('\x0b') [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x0804869d in password () gdb-peda$ === GDB END === Let's check the values in 0x804a02c. gdb-peda$ x/7x 0x804a02c 0x804a02c : 0x00000001 0x00000001 0x00000002 0x00000003 0x804a03c : 0x00000005 0x00000008 0x0000000d Oh, yes. seems the numbers got sorted as: int keys[] = {1, 1, 2, 3, 5, 8 ,13}; And then, let's interpret the last loop... 0x0804869d <+301>: movl $0x0,-0x8(%ebp) <-- 1. next loop... 0x080486a4 <+308>: cmpl $0x7,-0x8(%ebp) <-- 6. exit condition; jge 7, <7. 0x080486a8 <+312>: jge 0x80486ee <-- 2. JUMP From 1-6, Code: for(int i=0; i<7; ++i) { 0x080486ae <+318>: mov -0x8(%ebp),%eax <-- 7. eax = i; 0x080486b1 <+321>: mov 0x804a02c(,%eax,4),%eax <-- 8. eax = array[i]; 0x080486b8 <+328>: mov -0x8(%ebp),%ecx <-- 9. ecx = i; 0x080486bb <+331>: cmp -0x34(%ebp,%ecx,4),%eax <-- 10. ebp_34, was a buffer of our integer input, eax = buffer[i]; cmp instruction, we will create an 'if' condition, because +363 does not have jump back (if jump back is there, it's a loop..) 0x080486bf <+335>: je 0x80486db The comparison is done between buffer[i] and array[i], and let's negate equal, if(buffer[i] != array[i]) { 0x080486c5 <+341>: lea 0x8048823,%eax 0x080486cb <+347>: mov %eax,(%esp) 0x080486ce <+350>: call 0x8048370 gdb-peda$ x/s 0x8048823 0x8048823: "Oh no that's not the key\n" Code: printf("Oh no that's not the key\n"); 0x080486d3 <+355>: mov %eax,-0x48(%ebp) 0x080486d6 <+358>: jmp 0x8048704 <-- 12. NOT a jump back. jump forward to +404 from 12 and 13, it goes to the end of the program and returns. return; } <-- if condition ends. Otherwise, continue the loop... (see below).. 0x080486db <+363>: jmp 0x80486e0 <-- 11. JUMP target of +331, 0x080486e0 <+368>: mov -0x8(%ebp),%eax <-- 5. Loop increment, i += 1. 0x080486e3 <+371>: add $0x1,%eax 0x080486e6 <+374>: mov %eax,-0x8(%ebp) 0x080486e9 <+377>: jmp 0x80486a4 <-- 4. JUMP back, so it's LOOP! 0x080486ee <+382>: lea 0x804883d,%eax <-- 3. JUMP TARGET After the loop ends, which means that all seven integers meets the condition: array[i] == buffer[i], then 0x080486ee <+382>: lea 0x804883d,%eax 0x080486f4 <+388>: mov %eax,(%esp) 0x080486f7 <+391>: call 0x8048370 gdb-peda$ x/s 0x804883d 0x804883d: "Great! you got my password!\n" printf("Great! you got my password!\n"); 0x080486fc <+396>: mov %eax,-0x4c(%ebp) 0x080486ff <+399>: call 0x8048500 Run get_a_shell(); 0x08048704 <+404>: add $0x54,%esp <-- 13. seems its the program's end. (return;) 0x08048707 <+407>: pop %esi 0x08048708 <+408>: pop %ebp 0x08048709 <+409>: ret End of assembler dump. gdb-peda$ Let's collect all the code... >>> REVERSE ENGINEERED CODE printf("Send me 7 lucky integers\n") int i = ebp_8; // It's loop with i+=1 (because of 4.), so let's create 'for' int buffer[]; // at ebp_34 for(i=0; i<7; i++) { // jge, which is >=, and we negate that to < to not to jump. printf("Integer %d: ", i+1); scanf("%d", buffer[i]); // this will be a better representation. printf("Integer %d is %d\n", i+1, buffer[i]); } array[7] = 7; for(int i=0; i<7; i++) { for(int j=0 j<6; ++j) { if(array[j] > array[j+1]) { int t = array[j]; \ array[j] = array[j+1]; |------> a swap(array[j], array[j+1]) operation array[j+1] = t; / } } } for(int i=0; i<7; ++i) { if(buffer[i] != array[i]) { printf("Oh no that's not the key\n"); return; } <-- if condition ends. } printf("Great! you got my password!\n"); get_a_shell(); <<< Can you run 'get_a_shell()' now?