I wrote a small piece of code that interacted with the Twitter API to get the URL of an image included in a tweet, it’s 267 lines long - filesize is not particularly big either - 6.3K.
1 2 3 4 5 |
|
The go build
command,, by default will compile with Debug information, and Symbols. Additionally, it will include the Go runtime, so that the binary will execute in environments that do not have Go installed.
Here’s the above sourcecode compiled with the default go build
command
1 2 3 4 5 6 7 |
|
6.9 MEG FROM A 6.3K SOURCE FILE ?! OK, so lets take a look at what it is and what it includes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
|
So, debug information, symbols etc. It is possible to remove all the DWARF information using go build -ldflags '-w'
which will create us a slightly smaller binary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
Now we’re down to around 5M, with no debug information, but the file is still quite big, so the next step is to remove the symbols when compiling with go build -ldflags '-w -s'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
|
We’re getting somewhere, but the issue here is because the Go runtime is included inside every compiled Go binary. If you plan on using the binary within environments that do not have the Go runtime installed, this is about as small as you could natively make this binary (without packing, of course - we’ll get to that later). If you plan on only using the binary where the Go runtime is installed, we can use gccgo
to compile the source, and omit the inclusion of the runtime
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
|
We can get even smaller by combining everything we’ve learnt above, substituting -ldflags
with -gccgoflags
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
|
So, now we have two binaries, one with the Go runtime, and one without (you can see the size difference).
1 2 3 4 5 6 7 |
|
To get these files smaller, the only real option here is to pack them with something like upx
- the best compression can be obtained using the --ultra-brute -9
arguments. Here are the two files packed with upx
. But, before we upx
, we have to “fix” the files using goupx
as you will experience a EOFException: premature end of file
error when compressing. We also don’t want it to run the upx
process, because we’re going to pass some custom parameters to it.
1 2 3 4 5 6 7 |
|
Now to the packing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
This results in the following filesizes
1 2 3 4 5 6 7 |
|
Stripping and packing a Go binary in the above way is not recommended and does not guarantee that the binary will function afterwards - I have had about a 50/50 success rate (as an example, the binary shown here doesn’t work when compiled with gccgo, whereas the stripped and packed binary that includes the Go runtime continues to function perfectly. YMMV)
So, the lesson here is that you shouldn’t expect Go binaries to be small, and it’s not a good language to learn if you want small applications - use another language for that. Go is fun if you want to learn a new language for personal tools.
]]>I don’t want to tell you how to do this bit, so here’s the output
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Visiting the HTTP server without specifying a hosts entry will provide you with some instructions… however once a hosts entry is created, you can reference the VM correctly. So, I hit it with a browser to see what I was faced with.
None of the links work on this page, so I’m stuck just looking at this. Using nc
, I saw that the page is hosted via Varnish.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Varnish is a caching server, and has a PURGE HTTP option which cleans out any caches. Also note the GovPurGe header.
My next step was to do some enumeration on the page. There are 3 reviews, one of which is in pseudo-latin. However, there are 4 words that immediately stood out to me due to strange captitalisation. Steg
, Hide
, Varnish
, and Purge
.
I already know the server is Varnish, and I know about the PURGE option. However, steghide implies that something is hidden in an image…
So, an ominous message… I’ll note that down for later as it is not required right now.
There’s nothing else on this page of interest, so I decided to see what would happen if I send a PURGE request to the main page using curl
Once the PURGE completed, I visited the page again with a browser, and was presented with different page content.
Interesting. I also discovered that the image at the bottom of the page, which I previously ran through steghide had a different filename. Maybe there’s something different inside it ?
Different data - that’s interesting. The Youtube video linked is Knocking on Heavens Door by Guns N Roses, however the random characters after the URL are HEX, which decode to 2005, 31337, 1995, and 22 in decimal. Sounds like port knocking to me which opens SSH access, but I don’t have any usernames, passwords, or private keys to use.
Remember the message we obtained from the first JPEG file ? The reviewers are fortunate to have homes in today's purged economic climate.
. This implies that we might be able to browse the home folders of the various reviewers. Looking at the original, and purged versions of the front page, it is possible to obtain a list of possible users. Bill Williams0n, Zoey Sand1n, James Holmes, Charlie Hanger, and Bin Ladin. Of these users, only two home folders are accessible.
What an interesting Youtube video
However, the HTML source is much more interesting
1 2 3 4 5 6 7 8 9 10 11 12 |
|
This didn’t tell me much, so I decided to PURGE it too using the curl
command from earlier. This resulted in the following page being made available
The Youtube video is even weirder, but the source changed too.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
That, to me, looks like a private key. So, back to port knocking.
Once the private key has been saved into a file and chmodded correctly (600), I use knocker
to knock the ports, which allows SSH access.
So, now I have a shell. More enumeration required ! Wandering around the file system identifies that bwilliams0n has access to /home/zsand1n. Within that folder is an archive encrypted with PGP.
1 2 3 4 5 6 7 8 9 10 |
|
To decrypt this file we’ll need a copy of the private key used by the encrypting user.
There is a file in public_html that includes a public PGP key, but this is no help to us at all.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Could I use my old friend PURGE and see if the file changes ? Why yes, yes I can… and yes, it does change - I now have a private key instead of a public one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
This can now be imported into GPG and used to decrypt the archive found earlier. Remember the GovPurGe header from earlier ? It contains the passphrase required to use the key.
I spent a lot of time looking through the files from this archive, and eventually found a password of “vi.isforleeth@xxors” hidden in .bash_history in the jholmes directory.
This allowed me to su as the jholmes user and discover that he is able to run the /usr/bin/varnishadm
application via sudo
. I also discovered that, with a few tweaks, you can get varnishadm
to compile inline C and run commands as the user varnishd is running as. The last video shows me switching to the jholmes user, creating a malicious shell script to grab a copy of the dash binary from my attacking VM (dash does not drop privileges like bash does, therefore is a much better option for suid shells), running sudo varnishadm
, and configuring varnish to run as root
instead of the varnish
user. I then configure the cc_compiler parameter to run the malicious script I created earlier, which allows me to drop to a root shell and read the /FLAG.txt
file.
Done.
]]>There are 5 challenges, I am going to post them as I crack them.
NMAP shows that only 2 ports are open, 22 and 54311. 22 is obviously SSH, whereas 54311 is… ?
1 2 3 4 5 6 7 |
|
OK, we don’t know the password, and bruting it might be a bit excessive (though I did run it through rockyou to no avail). I spent a lot of time looking at this, and found out that the response time for a password of “a” was a lot quicker than the response time for a password of “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”. Maybe the application is parsing the password, and the answer is a timing thing ? Maybe I can work out the characters based on how quick the application responds to my input.
I wrote the following Python script to get an idea of timings for each letter while trying to guess the first character of the password
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
This produces an output like the following
It is sensible to hazard a guess that the first character is “R”, due to it responding a lot quicker than all other characters attempted. I then wrapped this into a script to brute the whole password
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
This script produces the following output
As per the video above, once the bruted password is entered into the application, we’re provided with a shell.
Onto the next step !
]]>People generally work alone on VM’s, so to mix it up a bit, I decided to team up with barrebas (blog) and own the VM as a collaboration :)
So, here’s a quick walkthrough on how to root Pegasus, written by both barrebas and myself.
An NMAP scan shows that the VM only has a few ports open that are of interest - 22 and 8088
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
8088, when visited with a browser, shows a lovely picture of a Pegasus. A quick look at the source doesn’t reveal anything, and there’s nothing hidden in the image file.
Time to brute force some directories/files. Experience has shown me that vulnerable VM creators are sneaky gits, so I opted to use a large dictionary here, just to see what it came up with. Because of this large dictionary, I had to use dirbuster instead of dirb, because dirb takes ages to parse large dictionary files. Prepare for some horrible UI screenshots…
I’m only interested in the files that returned HTTP 200, as these actually exist, so submit.php and codereview.php
codereview.php POSTS to submit.php, so for the moment I can ignore submit.php and focus on codereview.php
Mike is a code reviewer, and a trainee… therefore is pretty inexperienced. After a bit of time throwing various languages at the application, I found out that if you provide C sourcecode, it gets compiled and executed. Nice ! Lets bash some shellcode in there - specifically a bind shell and submit it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
A quick NMAP scan confirms port 4444 has been opened.
1 2 3 4 5 6 7 8 9 10 11 |
|
A quick connection to the port via Netcat and a bit of Python allow us to get a TTY enabled shell.
1 2 3 4 5 6 7 |
|
Now over to barrebas for the next step ! fancy screen wipe animation
So as user “mike”, I started poking around in the setuid binary “my_first”. It seemed to be some sort of C program with several functions:
1 2 3 4 5 6 7 8 |
|
The mail in /var/mail/mike mentions a git repo with the source code. We started attacking the binary without looking at the code, because the vulnerability jumped up quickly. The third option was not implemented and the reverse string operation seemed to be secure. I then went for the calculator, entering:
1 2 3 4 5 |
|
That seemed promising. I entered:
1 2 3 4 5 |
|
And we have our format string vulnerability! The basic idea now was to abuse it and overwrite a got pointer. I chose printf as the target and I wanted to overwrite it with the address of system. ASLR was enabled on pegasus, but because it is a 32 bit box, we can easily “fix” this with ulimit -s unlimited
. This enlarges the stack and fixes the address of libc:
1 2 3 4 5 |
|
Finding the address of system within gdb was trivial. The got pointer address can be found using objdump:
1 2 3 4 |
|
So it’s at 0x8049bfc. Now we needed to find the start of the format string on the stack. Recrudesce quickly identified it as argument number 8:
1 2 3 4 5 |
|
So I got working on an exploit. I quickly came up with this python script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
The format string first writes some dummy bytes and then overwrites the first part of the got pointer. It takes the 8th argument off the stack and uses %hn to write a half-nibble to that address. The value is the number of bytes that have been written.
Then, it takes the 12th argument, which is the pointer to the second half of the got entry. It writes some dummy bytes and then the outputs the number of bytes written to the got address. Effectively, after running the exploit, the memory location at 0x8049bfc now contains 0x40069060. This is the address of system in libc after running the ulimit trick.
So if we run this exploit, the next time printf() will be called by the binary, it will call system() instead!
1 2 3 4 5 6 7 |
|
OK, we have system() being called! So to fully exploit it and grant us a shell, we make a symlink to /bin/dash and call it “Selection:”. Finally we need to set the PATH environment variable so that the shell searches in the current directory and finds our symlink. The exploit is pushed to the binary via stdin and the cat command then catches the shell that is being spawned (otherwise it closes immediately).
1 2 3 4 5 6 7 8 9 |
|
So we now have a shell as john! I wanted to spawn another shell (using python) to get a pty, but it wouldn’t let me:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
This is probably because our little trainee “mike” is not a real person and is using up all our pty’s! No problem, we thought, let’s upload our ssh keys… only that failed, because our gid is set to mike and not john. Hmmm.. I wrote a small C wrapper to try and set gid and uid to 1000 (john) but it wouldn’t let me set gid.
1 2 3 4 5 6 7 8 9 10 |
|
But this did have the nice side-effect of allowing us a to spawn a pty shell!
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Nice! Now we can see that john is allowed to start the nfs daemon… Interesting, because /etc/exports lists the following entry:
1
|
|
no_root_squash… we can mount it remotely and have our own uid! NFS will not set it to nobody:nobody…
Over to recrudesce for the last bit of pwning pegasus!
Before I continue, lets hear it for barrebas and his exploit dev skills.
So, NFS huh ? What can I do with that ? thinks… well, I can mount it remotely and drop a file as root on my Kali box, suid the binary and execute it on Pegasus as john.
1 2 3 4 5 6 |
|
OK, so a quick side note here - my Kali box is 64 bit… if it were 32 bit I could just copy /bin/sh to /mnt/nfs and suid it. So, in this case, I have to use a C wrapper to execute a shell instead.
The code for the C wrapper is pretty straight forward
1 2 3 4 |
|
This is then compiled as a 32 bit binary, dropped into /mnt/nfs on my Kali box, and chmodded to 4777
1 2 |
|
Which, when executed as user john, drops me to a root shell
1 2 3 4 5 6 7 8 9 10 |
|
Allowing the grail of grails… the ability to cat /root/flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Connecting to port 1337 results in 3 numbers.
1 2 3 |
|
I guess I’m knocking some ports then. NB: in the test version, these 3 ports were in order, in the release version they’re randomised. So rather than knocking them in the order returned, we’re going to have to write a python script to do all possible port orders. The fun thing here is we don’t need to check anything after each knock attempt - we just knock the port numbers in all possible combinations and then check afterwards for any new ports.
I couldn’t be bothered to write my own script, so I stole Leonjza’s, as I was talking to him about it at the time
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
He’s a clever one :P
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
Now another NMAP is required to see what opened (if anything)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
The HTTP server is hosting a simple page, which displays an image of a door, and text asking us to look deeper.
Now, if that’s not an invitation to look inside the JPG, then I don’t know what is.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Ooh, some possible credentials. They look like ROT13, so…
1 2 3 |
|
OK, that’s just Jason reversed, so I guess the same deal for the password.
1 2 3 |
|
These credentials can be used to SSH into the VM, which allows us to take a quick look around.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Looks like we’re in a restricted bash shell, so lets escape that quickly
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
OK, so the next part is probably explained a lot better by reading some IRC logs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
|
So, now that you read that, this is what my Python script looks like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
This generates a .tfc file, which when provided to the tfc application “decrypts”, jumps to esp and executes the shellcode, dropping us to a shell.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
A quick dig around using NMAP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Looks like there’s a banner on port 31337, might as well get all of it, and see if the service behind it is vulnerable to format string.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
This looks like something we’ll need a username and password for. As none are known, this port is being ignored for now. Port 80’s where it’s at then.
The Register link allows us to create a new user, which we can use to log in and look around
But it seems we’re not allowed to use the Upload feature, because we’re not “admin”. An interesting point to note here is that the URL for the Upload feature is http://172.16.56.131/uploadform.php?page=forms/form.php, which means we could possibly leverage it for LFI. No other links on the site work in this way.
Looking at the source for the registration page, it seems the username is capped at 16 characters.
1 2 3 4 5 6 |
|
Could we cheat the system and register a user with the name “admin” and then add 11 spaces after it plus a random character ? We can assume that the registration form will truncate any characters over 16. Tamper data is our friend here - the form can be filled in with the username of “admin” and then intercepted with Tamper data (or Burp, if you want) to modify the username POST variable to “admin a” and submitted.
Once this malicious user is created, it is possible to then log in with the username of “admin” and the password we set for “admin a”.
Which allows access to the Upload feature (not that we’re going to use it for it’s intended use)
The upload form posts to /application/upload
1
|
|
Which when called with no arguments shows the following
It is possible to browse to the application folder, which shows that the application is actually 601kb - a bit much just to parse an upload form.
The uploadform.php page can be used to provide us with a base64 encoded copy of the upload binary, which means it will not be parsed or executed.
With the binary now available offline, we can go about working out what it does. It’s a CGI binary, so we can interact with it via the command line using the QUERY_STRING environment value. Lets set something really simple first. I’ve purposefully not included any GDB stuff here, as an excuse for you to work it out and learn :)
1 2 3 4 5 6 |
|
It is possible to overflow this application via the email variable, which through some further investigation identifies that EIP is at offset 277.
1 2 3 4 5 6 |
|
gdb-peda can be used to find a jmp esp call, which is at 0x80c75ab9. This is what we’ll set our EIP value to, which should hopefully jump to our bind shell shellcode. This can be completely exploited as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Once a public key is added to authorized_hosts, we can SSH in and obtain a full TTY shell.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
There’s one application available in /home/rmp, and that’s the application that is listening on port 31337 (the one that requested username and password etc).
1 2 3 4 |
|
which when straced, shows that it is reading /root/password.txt to check the provided credentials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
However, by pure chance, in this particular attempt, I tried to overflow the privs command, and as you can see, it seems the application is attempting to open AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA before the password file. Can we use this to make the application open a file containing a password we control ? Yes, yes we can…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
As you can see here, it’s cut off the first 16 characters of our priv input, and is trying to open “rd.txt”, which doesn’t exist. Therefore we have to pad the privs input by 16 characters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
So, all we need to do now, is write a file to /home/rmp containing a password, and make the application read it instead of /root/password.txt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
I would put this VM at beginner level - it’s not particularly complicated. It’s more a case of finding hidden data than actually doing any vulnerability exploitation. Lets get started.
And so should you.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
FTP, SSH and HTTP. Lets hit the HTTP server first, as that’s where Tr0ll started.
So, the usual then. A quick Nikto scan shows that there’s a robots.txt file, which has the following in it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Using this as a wordlist for dirb shows that only 4 of these directories actually result in an HTTP response other than 404.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Visiting these URL’s provide the following page
While it looks like the page is identical for each of the 4 directories, on closer inspection, one of the images shown is slightly larger than the rest - this was determined by downloading each image.
1 2 |
|
Running strings (NOOOO !!!!) on this file results in the following output
1 2 |
|
Which just so happens to be a folder on the webserver, containing an answer.txt file. Short story shorter, answer.txt is just a dictionary file but with each individual line base64 encoded. On visual inspection, one line stands out… (yes, I could probably have written something, but scrolling through a large file and noticing strange anomalies is my bag, ok ?)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Decoded, we get
1 2 |
|
So, we’ve exhausted the HTTP server by this point, so lets move onto FTP. I won’t bore you with details, but the username and password combo is Tr0ll:Tr0ll. The only file residing on the FTP server is a ZIP file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
On extracting the ZIP file, we are asked for a password… lets try “ItCantReallyBeThisEasyRightLOL”
1 2 3 4 |
|
which turns out to be an RSA private key - this is our route in via SSH.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
It is possible to SSH into the VM using the noob user, as per the key’s filename, but we are immediately disconnected.
1 2 3 4 |
|
Standard things here like –norc don’t work, so lets try something new. SHELLSHOCK !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Yup, we can get a shell with this using ‘() { :;}; /bin/bash’
1 2 3 |
|
A small bit of enumeration here identifies the following folder structure
1 2 3 4 5 6 7 |
|
Each door folder includes a file called r00t, which is a binary. However, there are 3 different versions. One of them puts you into an rbash shell for 2 minutes, one of them kicks you out and reboots the VM, and the other one (the largest one) repeats anything you provide it via stdin. These files are SUID, so, looks like we have a standard buffer overflow here.
1 2 3 |
|
Something to note here before we carry on - a scheduled script rotates these files, so make sure you always work on the file that is 8401 bytes large - you may have to change into a different door directory.
Loading the file in GDB shows that you can easily overflow it with about 300 bytes of input.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Standard buffer overflow process here (I don’t want to teach you how to suck eggs tbh, there’s enough resources online). EIP is at 269, so…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
OK, easy peasy. GDB identifies that there isn’t a jmp esp in the binary, but it is a dynamically linked binary, so ret2libc is possible. But why make it more complicated for ourselves ? Naaah. Our shellcode can be placed in an environment value
1
|
|
Which can be located within GDB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
Shellcode starts around 0xbffffe10, so we just change our EIP to that memory location to run the shellcode. As proven within GDB, it runs as expected and /bin/dash is executed (GDB strips EUID though)
1 2 3 4 5 6 7 8 |
|
When run outside of GDB, we get dropped to a root shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
root@flick:~# id
uid=0(root) gid=0(root) groups=0(root)
First ! @leonjza @VulnHub #flick #boot2root #vulnhub
Blah blah NMAP blah ;)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
SSH and a random unknown port - ominous. A quick netcat to the port presents us with a request for a password
1 2 3 |
|
Anything you type in just gets repeated back to you. I also checked for buffer overflows and format string vulns, but no avail - looks like I need a password.
1 2 3 4 5 6 7 8 9 10 11 |
|
OK, so not knowing the password, I decided to check out SSH.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
|
OK, that’s something - I’m intrigued to see what the hex decodes to
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Oooh, base64 - this has to be the password !
1 2 3 |
|
More base64… OK, decode again
1 2 3 |
|
Turns out you have to keep decoding the base64 about 13 more times until you end up with this
1 2 3 |
|
Which just so happens to be the password for the application runnning on port 8881
1 2 3 4 5 6 7 8 |
|
Another NMAP scan now indicates that port 80 is open. Let us take a look.
Looking at the site hosted by the website kinda stops me in my tracks. KITTENS !!! YAY !!!
You can log in with the credentials of demo:demo123 on the login page to give you the ability to upload images (note here, it allows you to upload absolutely anything).
I figured that the upload feature was the vulnerability and worked on uploading PHP shells etc, but I was not able to get any PHP to execute. Maybe it’s not the upload feature that’s useful, maybe it’s the download feature ? The download feature will present you with a file, called image.jpg, of the file you’re requesting. Turns out you can request any file on the filesystem if you bypass the directory traversal filter (same way as with Hell, using ….// instead of ../). Requesting the following
1
|
|
results in an “image.jpg” file which needs to be catted to view the contents of /etc/passwd as text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Hmm, with this I can request pretty much anything, right ? If a folder or file exists, it gives me a download dialog, if the file does not exist, it gives me an error. This is blind filesystem traversal.
Firstly I needed to find out the DocumentRoot from the Apache
So, I figured I wanted some credentials - and started by trying to get MySQL credentials from the application. A bit of googling determined that the application was Bootstrap on top of Laravel. Firstly I requested the following URL to get the apache configuration.
1
|
|
which pointed me towards the sites-enabled folder. A bit of googling told me that sites-enabled is just full of symlinks to files in sites-available, so I grabbed the default file
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Our DocumentRoot is /var/www/flick_photos/public, so we now know our base path. Using the Laravel github repo as a directional aid, I found out the database config file is located in app/config/database.php, so
1
|
|
catting this image.jpg file does indeed show us credentials for MySQL, but there’s something more interesting
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
|
This line is what we need to be focusing on
1
|
|
which would indicate that the app previously used SQLite, and switched to MySQL. There’s a file I need to get my hands on !
1
|
|
Yes ! Once I renamed the file and installed SQLite tools onto my Kali VM, I was able to wander around the database to get some username and password combos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
I know that the users Robin and Dean exist, so I decided to try the above passwords
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
OK, we’re in. Let the fun continue.
Dean’s home folder includes two files - a message text file and a binary called read_docker with the SUID bit set as the robin user.
1 2 3 4 5 |
|
The message file tells us that Robin is away on holiday, and that the admin guys will allow Dean access to his dockerfile in his home directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Looks like they tried to be clever and made the binary instead. So, what does this binary do ? It seems to just read any file called “dockerfile” from the directory you provide.
1 2 3 4 5 6 7 8 9 |
|
After messing around reading files I created in varying directories, I decided to fool the application into reading a symlink to an arbitrary file on the filesystem - my first attempt was /home/robin/.ssh/id_rsa. Might as well start somewhere, right ? I was fully expecting this to fail, as I had no idea if the target file actually existed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
OK, so, I’ll put that into a file then and log in as Robin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Robin can sudo /opt/start_apache/restart.sh without a password, as shown by running sudo -l
1 2 3 4 5 6 7 |
|
This shell script seems to restart the Apache servers when run
1 2 3 4 5 6 7 |
|
The file cannot be viewed, as the permissions prevent it
1 2 3 4 5 |
|
There’s got to be another way to read that file to see what it’s doing. Further poking around resulted in me finding out that Docker is installed (Docker is quite cool - take a look). However, as with any application, there are vulnerabilities.
The docker version command shows us the server is running version 0.11
1 2 3 4 5 6 7 8 9 10 11 |
|
Which, according to this site, is vulnerable to a container breakout. The blog indicates that there is PoC code out in the wild - so off I went to find it. Turns out it’s on github, obviously.
OK, so lets clone the repo to the local machine and have a look at the code
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
|
Looks like it’s a simple case of replacing the following line with the file to read
1
|
|
I thought I could be sneaky here and request /root/flag.txt, so I edited the source code, and compiled it as per the instructions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
Wow, 1 it worked, and 2 this is not the flag you are looking for
I did notice, however, that it provides a directory listing when trying to find the file requested. There’s a folder or file called 53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc. I wonder if that is either the flag, or if the flag is in it ?
Firstly lets try requesting it as a file by changing the source code, compile and run it
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
It’s a directory, so we need to know the file name. If I request a random filename, I will still get a folder listing, so changing the source code to
1
|
|
results in the following when compiled and run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
Our flag is actually called real_flag.txt, so a quick edit, compile and run, and we have the flag !
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
So, on Vulnhub, the text for Flick has this line in it
1
|
|
Using the aforementioned Docker Container Breakout vulnerability, I read the contents of /opt/start_apache/restart.sh
1 2 3 |
|
OK, so, it’s restarting apache and restarting all processes controlled by Supervisord. Supervisord stores config files in /etc/supervisor/conf.d. Using the Docker vuln to blind enumerate the files, I located /etc/supervisor/conf.d/start_apache.conf which I read
1 2 3 4 5 6 7 8 9 10 11 |
|
/usr/bin/supervisorctl restart all is running /opt/start_apache/start.py. That’s the next file I read with the Docker vuln
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
|
This script is the script that provides the service found on port 8881, and seems to get it’s config from /tmp/config.py, and if that doesn’t exist, /etc/config.py. I read /etc/config.py to get an idea of the syntax
1 2 3 |
|
Pretty simple. I can use this to execute a set of commands by creating /tmp/config.py with the following contents - this will take a copy of /bin/sh and put it in /tmp called rootshell, then set the SUID bit.
1 2 3 |
|
Now, when I run the /opt/start_apache/restart.sh script, /opt/start_apache/start.py will be run, which will result in /tmp/config.py being read instead of /etc/config.py.
1 2 3 4 5 6 7 |
|
Now, we know that typing the correct password runs the command - we know this firstly from the source code, and secondly because we used it to start the web server right at the beginning. All I should need to do is connect to port 8881, and type the correct password. It should then run my malicious command.
1 2 3 4 5 6 7 8 |
|
Fingers crossed there’s a rootshell file in /tmp
1 2 3 4 5 |
|
Well, what do you know - it worked ! Running /tmp/rootshell drops us to a root shell, where we can properly read the flag, and put our public key into authorized_keys.
1 2 3 4 |
|
And for completeness, an SSH session as root.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
root@xerxes2:~# id
uid=0(root) gid=0(root) groups=0(root)Yup. #vulnhub #xerxes2 @barrebas @VulnHub](https://twitter.com/VulnHub
Usual stuff here, it makes sense to do some enumeration. The standard route is to start off with an NMAP scan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
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 ?
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.
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
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.
1 2 3 4 5 6 7 8 9 10 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
This is Marie Delacroix - creator of Sarah which Xerxes was employed to run.
First things first, lets get a list of possible users from /etc/passwd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
The delacroix user has source code for an application in their home folder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
|
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.
1 2 3 |
|
Yup, it’s Brainfuck. Shit.
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.
1 2 3 4 5 6 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
printf is at 08049a48. Now I have to find system(). gdb can help with that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
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
1
|
|
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
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
1 2 3 |
|
chmod +x the file so it is executable, and then run the following
1
|
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
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
1 2 3 4 5 6 7 8 9 |
|
Say hello to Janice Polito, she’s got a smirk on her hasn’t she ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
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 !
1 2 3 |
|
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.
I used pdf-parser to see what the PDF contained
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
I used a hexeditor to add the PDF header and footer information
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
I was then able to run this through pdf-parser again to get to object 4. Now, I wont show you that, what I will do is show you this conversation…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
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.
1 2 |
|
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.
1 2 3 4 5 6 7 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
|
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.
1 2 3 4 5 6 7 8 9 |
|
Looks like Korenchkin is sensible and backs up his keypair… but now I have it, so user impersonation is possible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Once again, my public key is echo’d into .ssh/authorized_keys, and I am able to SSH in from my client.
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
1 2 3 4 5 6 7 |
|
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
I realised I needed a Makefile as well, so I knocked one of those up quickly (nothing complicated)
1 2 3 4 5 6 7 |
|
With the source code and the Makefile, I then compiled the kernel module and loaded it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
I created another Makefile, compiled the module and loaded it
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 |
|
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Obviously NMAP is used to get a quick rundown of what services are available.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Not very exciting - SSH, HTTP and a SQUID proxy. Interestingly SSH is filtered, which means no easy route in :(
HTTP is our next destination, which shows a nicely branded login page.
SQLi me-thinks. Lets try some simple things like entering 123’ or 1=1 into the email field and clicking Login
Hmm, no dice. Looks like the code is filtering out simple SQLi. Can we fool this into filtering out the ‘or’ and the ‘=’ but still have an ‘or’ clause left over ? Turns out, yes we can ;)
This requires a GIF…
We now have SSH login credentials, but you’ll remember earlier on that SSH is filtered externally. It is, however, possible to leverage the SQUID proxy to get an SSH session using proxytunnel to bind a local port.
1
|
|
A quick netstat shows that our local port is available
1 2 3 4 |
|
Let us SSH into it with the provided credentials
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Seems like the admins have been clever, and have edited this users .bashrc file to terminate the session upon connection. It is possible to use the -t argument of the SSH client to execute an application when a connection is made, so how about running a shell ?
1 2 3 |
|
First things first, that SQLi filter - what did it look like in it’s entirety ?
1 2 3 |
|
Quite simple, but annoying none the less. Thankfully it was easily bypassed. It’s obvious while looking at the /var/www/login.php file that the application uses a MySQL database to handle logins. The MySQL credentials are helpfully in plain text within the file
1
|
|
Using the mysql client, it is possible to obtain all users passwords, as they are stored unencrypted in the login table within the SkyTech database
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
That’s handy - William looks like he’s revered by the company, as his password is (a typo) of ‘sensible’. Unfortunately his credentials do not work
1 2 3 4 |
|
How about Sara ?
1 2 3 4 5 |
|
We’re hitting on the same issue that we had with John - SSH -t to the rescue once again, but this time using a different shell (for some reason /bin/bash does not work)
1 2 3 |
|
Sara has been entrusted with sudo ability, albeit very locked down, but the available commands running as the root user can be used to leverage directory traversal to read files elsewhere on the filesystem. Files such as /root/flag.txt, which provides us with the root password
1 2 3 4 5 6 7 8 9 10 |
|
A quick su root drops us to a root shell
1 2 3 4 5 |
|
A lot of the techniques in this VM are known to me apart from the very last step. I will go through my thought process for each step and how I managed to go from enumeration to a root shell.
@0x42424242 Finally rooted your fucking VM :P
NMAP - the start of any fulfilling enumeration stage. I ran a simple NMAP scan against the VM, which returned the following results
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
OK, so we have SSH, HTTP and apparently Doom (yay Doom !) servers running. Based on previous experience, port 666 is more likely to be a service that is hidden behind a well known port. I wont go into the details of port 666 nor the service behind it, but lets say it’s a distraction and a diversionary tactic - don’t go there unless you want to waste time. Unless you want to waste time, in which case fire up Netcat and go for your life !
Visting the web server in a browser presents a pretty boring page that welcomes us to the server and shows a cartoon.
This doesn’t give us much to go on, therefore let’s try out some simple things any pentester should try - let’s see if a robots.txt file exists. Well what do you know…
The /personal/ folder shows a creepy shrine site (this is incidentally the closest I’ve ever been to the face of a cow)
Yeah, creepy… The /super_secret_login_path_muhahaha/ folder presents a login page
It is assumed the username is either admin or jack, and the password relates to g0tmi1k in some way because this Jack guy is quite smitten. I could run Rockyou against the login page, but let’s think simpler and first make a word list from the shrine site and mutate it a little with John The Ripper using a modified ruleset as per netsec.ws
1 2 3 4 5 6 7 8 9 10 11 |
|
Now we have a password list, we can use hydra to brute force the login.
1 2 3 4 5 6 7 8 9 10 |
|
Success, username jack, password g0tmi1k69 (yes, more creepiness, this Jack guy needs locking up). Using these credentials presents us with a management style interface
I won’t explain each section, but the two sections we are going to use are Notes and Personal. Notes allows us to write arbitrary text to a note.txt file stored in “temporary storage”, which is assumed to be /tmp as this is usually what people define as temporary storage.
Personal presents another login page, which sets a cookie which has an attribute that increases with each failed login.
When you hit 3 failed logins, the page reverts to the main panel, and adds an error message to the top.
On further investigation, the intruder attribute of the cookie is set which defines the file that is included at the top of the panel. I wonder if there’s a local file inclusion vulnerability there. I went about editing the cookie to try and include other files on the filesystem. After a few minutes of frustration, I worked out that the code was filtering any instance of ../ in the include path - however, being the sneaky person I am, I discovered this can be bypassed by using ….// or …/./ instead. Here’s /etc/passwd included by setting the intruder cookie attribute to ….//….//….//….//etc/passwd
It looks like the page parses any code (HTML etc) in the included file, therefore we can poison the note.txt file with PHP via the Notes feature, and include it to execute the code. First, a PHP based reverse staged Meterpreter is created and hosted on an HTTP server.
1
|
|
As we are using a staged payload, a handler is required, therefore one is set up via Metasploit
1 2 3 4 5 6 7 8 9 10 11 |
|
Once the failed login cookie is cleared to allow us to get to features again, the following is added to the note.txt file via the Notes feature, which will download our payload from our HTTP server to the /tmp folder on the host
1
|
|
Three failed logins are once again performed via the Personal feature to create the cookie, which is modified to include /tmp/note.txt (which includes our download PHP command)
When the main panel page is reloaded, our poisoned note.txt file is included, the PHP is parsed and our payload file is downloaded.
We’re onto something here ! The cookie is edited once again to include /tmp/sneaky.txt and the panel page reloaded to execute stage 1 of our Meterpreter payload, which makes a connection to our waiting handler.
1 2 3 4 |
|
Now would be a good time for a party, but there’s hacking to do. Plus I have no cake or balloons. Or friends. whimper … HACKING !!!
The Metepreter session can be used to get a shell, which doesn’t seem to be TTY, but we can fix that with a nifty command thanks to g0tmi1k himself (who is presumably hiding from Jack, the creepy so-and-so). The id command shows that we are currently under the context of the www-data user.
1 2 3 4 5 6 7 8 |
|
BAM ! We’ve just started the journey.
Catting /etc/passwd gives us a list of users that are potentially useful
1 2 3 4 5 6 7 8 9 |
|
Escalating from the www-data user to the jack user is pretty inconsequential. People who write web sites, especially disturbing ones such as this, will usually not bother to create separate accounts for things such as database logins.
A quick grep on the files in /super_secret_login_path_muhahaha/ for the word “password” results in the following
1 2 3 4 5 6 |
|
Oooh, what do I spy with my little eye ? Could that be Jack’s password commented out in login.php ? Let us try this password…
1 2 3 4 5 6 |
|
Well, that was easy. But then the 2nd hurdle usually is - you kinda get cocky after clearing the first one, that the second one is jumped based on pure thrill. Now we get to trip over on every hurdle between us and the finish line. Get the first aid kit ready, we’re going to need it.
Poking around Jack’s home folder is like walking into a public toilet while bursting for a wee - you don’t really want to touch anything, but have to else you’re going to have problems.
The first thing that draws my eye is the g0tmi1k_pics folder. It’s somewhere you know you don’t want to go, but seems to have this strange come-hither vibe about it. It’s like a car crash… gruesome but you can’t help but look. So I did. Now I wish I didn’t. Exhibit A, and B, and C.
Yeah… Jack, you’re a strange one, I’ll give you that. Hang on, I think I can hear the men in white coats knocking. Yup, they’re asking for you - shall I tell them you’re “out” ?
So, while Jack might reuse passwords, he’s sensible enough to use PGP when sending emails. /var/mail/jack/received contains an email, which is encrypted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
As suspected, Jack uses PGP on the local host, therefore his PGP keys are stored in /home/jack/.pgp, complete with a note giving us a password hint.
1 2 3 4 5 6 7 8 9 |
|
We can assume the password is either g0tmi1k69 or zgcR6mU6pX. GPG is installed, so the next logical step is to try and decrypt the email using either of these two passwords and Jack’s PGP private key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Thank you, Jack, for being a predictable - yet persistent - weirdo. A quick hop and a skip over to the worryingly named milk_4_life account and we can carry on.
1 2 3 4 5 6 |
|
The home folder for milk_4_life is pretty sparse, just a binary called “game”. However, it’s owned by the george user, and has the suid attribute set.
1 2 3 |
|
Running the binary produces the following output, which doesn’t tell us much other than it’s “listening”. Like a overly intrusive neighbour.
1 2 |
|
Netstat isn’t available on this VM, so we have to find another way to obtain listening port information. Thankfully ss saves the day and shows the game binary IS actually listening, on port 1337 - h4x !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Connecting to the port from another session asks us to type “START” to begin. It’s more fun to troll the code, though, by typing something completely different. Did you expect anything less of me ?
1 2 3 4 5 6 7 8 9 10 |
|
OK, enough messing around, let’s see what this game does. Typing START presents us with a high score, and then math questions that only your high school math teacher would ask you infront of the rest of the class. Needless to say I’m not answering any of these correctly !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Being the competitive person I am, I figured it would be fun to try and beat the score. More than 133723 points in 30 seconds means I have to answer 4457 correct answers a second. I can type quickly, but not that quickly. This requires some scripting - Python to the rescue ! This script connects to the game, reads the question, splits it based on “ x ” to get both numbers, performs the multiplication and sends the answer back. Granted I had some help on with this code - thanks c0ne !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Running it produces a sense of authority over our robot overlords - I’m gaming the game.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
It seems that the binary performs an action when the highscore is beaten. I’m not sure what it chmods, but I wonder if we can play this game at it’s own game and make it run a bogus binary. There’s a distinct possibility the code is relying on the PATH environment variable and just calling system(“chmod file”) rather than using an absolute path.
So, a bogus chmod binary is created using the following source, compiled, placed in /tmp on the VM and made executable (do I need to show you how to do this ? No ? Good. Saves me the typing.)
1 2 3 4 5 6 7 8 9 10 |
|
The path environment variable is modified to make /tmp the first location checked
1 2 3 |
|
So, hopefully, if all goes to plan, when we beat the score it should run our fake chmod binary and drop us to a shell. How about we give it a go ? The game is started, and the Python script is run to beat the score (I’m not going to show you again, just scroll up if you want to see what it looks like).
The score is beaten and we are indeed dropped to a new shell as the george user.
1 2 3 4 |
|
George has one file in his home folder - a Truecrypt container. I guess no one has told George that TrueCrypt isn’t recommended any more - he should be using something else. shakes fist at the NSA
Mounting the TrueCrypt container requires us to know the container password, which we currently don’t have.
1 2 3 4 5 6 |
|
Looking around a bit more, it looks like George has also signed up for RockYou
1 2 3 4 5 6 |
|
ka-ching George’s password will be on the leaked RockYou wordlist. oclHashCat can help us out here, thanks to the ability to use GPU’s. Yes, I’m using a Windows PC for this, sue me.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
That didn’t take too long now did it ? But we have the TrueCrypt container password, which makes mounting it a tiny bit easier now.
1 2 3 4 5 6 7 8 9 10 11 |
|
Hmm, a private key - lets try SSHing in as bazza using this key and see what happens.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Oh… I was expecting a bit more of a fight, but I guess Bazza trusts George, even though he uses defunct software that allows privilege escalation (look it up). Fool.
Once again we’re presented with 2 binaries, with SUID attributes set. This time, however, we can read the files, which means we can decompile them. Time to take a look
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
OK, so part1 runs part2 (this time with an absolute path), so time for a quick peek into part2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
So, part2 will run only if the effective group identifier is 1003. part1 has a SUID attribute set as group developers, which means you have to run part1 before part2. If you run part2 first, this happens
1
|
|
Running part1 changes our effective group identifier, and part2 changes our effective user identifier, but doesn’t seem to drop us to a shell as that user (oj)
1 2 3 4 5 6 7 8 9 10 11 |
|
A further look into the source code for part2 shows that it is not using an absolute path for the system(“id”) function, therefore we can trick this application into running a bogus id binary in the same way we did with the bogus chmod binary. The source code is pretty much the same, apart from the fact I’m using uid 1005 rather than 1000.
1 2 3 4 5 6 7 8 9 10 |
|
This is compiled, placed in /tmp and made executable. The path environment variable is modified as per last time and we’re good to go.
1 2 3 4 5 6 7 8 9 10 11 |
|
Success !
Nearly there, only this hoop to jump through and we’re done.
The OJ user has 1 file, a binary called echo which does exactly that, it repeats what you send it. This guy is the height of programming ability. There’s got to be something wrong with it.
A quick look at the binary in IDA, and yes there is something wrong with it, there seems to be something in it that is called a “format string vulnerability”.
wut ?
What felt like 12 days of reading, and video watching (Fuzzy Security videos are the best - check this one out for formatstr vuln explanation and the follow up) I still felt none the wiser. But I figured I’d give it a go anyway. What’s the worst that can happen ? I can’t make it any more wrong now can I ?
Meh, let’s put our shellcode (setuid plus /bin/sh) into memory anyway, just so it looks like we’re progressing.
1
|
|
So, I started to play around with gdb and format strings. There are several ways you can do format string vulnerabilities - you can put your shellcode in memory using an environment variable, then overwrite a global offset table entry to execute your code (as per the videos), or if you’re unlucky like me and have a statically linked binary, you have to find another way such as jumping to a location in the stack. I tried that… it didn’t work because my shellcode kept moving in memory.
Apparently, though, you can overwrite entries in the .dtor part of the binary - don’t ask me what .dtor is, but I had great fun gradually overwriting random memory locations and crashing the application over and over and over again…
Here are some of the format strings I fabricated while on my journey - you can get an idea of my thought pattern and the things I tested. It’s also pretty obvious when I went back to basics about 7 attempts in.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
I spent a lot of time staring at segfault after segfault until I stumbled upon this random combination of characters (I really really want to explain how I worked it out, but I’m not 100% sure myself. I guess that’s a follow up post sometime. Also, it is possible this wont work for you).
1
|
|
Which when executed like this
1
|
|
dropped me to a shell.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Like a baws. Now I’m going to exorcise the VM and purge every last shred of it from my SSD. BEGONE FOUL DEMON !
]]>The talks were brilliant and well organised, and the lock picking village allowed me to get my first taste in lock picking - I think I’ve gotten the bug now and have already ordered my first set of picks and practice locks. I wasn’t able to get to as many talks as I’d have liked, but that wasn’t an issue due to the people around me who were happy to impart their knowledge and answer any questions asked.
And Tod, if I see any weird transactions on my credit card in the next few days, I know who I’m going to ask ;)
The only slight issue with the day was not related to BSides at all - but it was due to the Tube Strike. I’d like to extend a personal thank you to all the TFL members of staff who continued to come to work today and assist in keeping London moving at a moderate, but slightly slower, pace than usual. I will not repeat what went through my mind as I walked down onto the platform at Earls Court only to see thousands of people - I couldn’t see one inch of the platform ! At no point did I come across a staff member who was nothing but polite and level headed about the whole situation.
After sitting on the floor of a very busy train for the nearly two hour journey home, I was able to reflect on the introduction I’d been given to the infosec industry; I mean I’m a novice - I’ve only been seriously thinking about this as a career path for around a year, if that. There’s a lot of people talking about Infosec Burnout at the moment, but I saw nothing of the sort. All I experienced were people who went out of their way to run and attend an event for other like minded people in the industry. Those people pulled out all the stops to make BSides London welcoming, friendly and a lot of fun. I hope the Burnout mentality is just a phase and that it passes quickly so we can continue to host events of this stature.
Here’s to next year - and here’s to BSides London getting bigger and better.
]]>Anyway, quick and simple post this one - I’ll be at both events. You can tweet me @recrudesce if you want to meet up and say “hi”.
If you’re going too, have a good time - remember to get loads of swag ;)
]]>After much research into the Certified Ethical Hacker course, the term “OSCP” kept cropping up in my google searches. OSCP… OSCP… what is this mythical OSCP of which you speak !?
Offensive Security Certified Professional. Possibly one of the most intensive self learning course I’ve had the privilege of spending my hard earned cash on. The only course that gives you a full lab environment to compromise - with no less than 50 VM’s of varying shapes, sizes, OS and vulnerability levels. Ranging from simple boxes that can be compromised with a cough, to boxes that make you tear your hair out for days while looking for a route in. I am not allowed to go into detail about the labs, or individual machines, but I can tell you there are some absolute brain teasers in there.
So, lets rewind a bit. Personally, I’ve not really spent that much time using applications like Metasploit or similar. I’ve got a fair amount of computer and network knowledge, plus a logical mind - how hard can the course be ? I read up on people’s experiences and it seemed to be the course for me (I have no significant other, or kids, therefore I could spend 6 hours every evening sat in front of my laptop - like a real hacker !), so I pulled out the ol’ BarclayCard and signed up. Now I was into the waiting game. You have to choose a starting date, on which you’ll receive an email providing course materials (more on that in a sec) and VPN configuration.
Connection to the labs is via VPN, which is incredibly stable considering the amount of traffic you send through it - this is a testament to the skill of the OffSec admins. Once you’re VPN’d in, the labs are your oyster.
The course material is very professional - a PDF of nearly 400 pages, plus over 150 videos - which cover varying pentest techniques from port scanning to SQL injection. It’s a bit daunting at first, but Muts walks you through each example with a soothing voice and with clear concise instructions. He’s like that awesome science teacher you once had at school - you know the one.
The techniques you learn in the videos/PDF are only a small part of the learning required for OSCP. It is expected that the student performs self study via other resources. Varying blogs, google searches, wikipedia etc can be used to expand your knowledge, and I found myself initially looking for a particular technique only to disappear down a rabbit hole of related exploits, techniques and vulnerability disclosure write ups. When they said “self study” they meant it !
Other than learning an immense amount of skills, I’ve met some amazingly clever people via the provided IRC channel (#offsec on Freenode). The channel is full of admins (who you “ping” when you need a hint), alumni and students. I found that certain people were always online when I was, and we naturally teamed up to pool our resources and excitedly boast about compromising the complicated boxes. Those people were also a sounding block for when the course got too much, or the outcome looked like it was completely fruitless. Sometimes just talking to these guys resulted in a different path opening up and a compromise minutes later on a box I’d wasted 3 days on.
As a personal challenge, I decided I would root every single box available to me, which I achieved. My final report (which you have to write and submit) was 388 pages long just for the labs ! The exam is the pinnacle of the whole process, where you’re given 24 hours to compromise a subset of boxes to get points. I’m not saying you chase after the numbers, but it’s very tempting to try and get every point possible.
After 8 hours into the exam (of which the latter 5 hours were just me going nowhere) I had compromised 1 box. I was ready to give up, but the aforementioned people I’d met in the channel urged me to continue and virtually slapped some sense into me. Within a further 2.5 hours I’d accumulated enough points to pass. I decided to quit while I was ahead and write my exam report. A further 40 pages of documentation was added, and submitted to the OffSec team along with my lab report. Now I had to wait for my result…
My result arrived on the 1st April, in the afternoon (I guess so it didn’t look like an April Fools joke). A pass - that’s all you’re told - there’s no score, no feedback on your report. Just that wonderful sentence that tells you you’ve passed and are now OSCP certified. Nice.
In the 4 months I spent doing the course I have laughed, I’ve cried, I’ve stressed out, I’ve jumped for joy. I think I experienced every kind of emotion. It was fun going into the office and printing out a new diagram showing the new boxes I’d compromised the previous evening. The thrill of being able to explain some of the techniques to people I work with and have them sit there in awe at the cool stuff I’d been doing.
So, what next ? Well - the next step is OSCE. But that’s for another day.
]]>