个人学习25.11.21 hnusec ctf-web week2
WEEK 2
分为学习笔记和WP
即HTTP和RCE的wp会在后方单独列出来
学习笔记
HTTP协议
概念
HTTP:全称HyperText Transfer Protocol(超文本传输协议)
用于在客户端(如浏览器)和服务器之间传输超文本数据
所谓超文本,就是包含超链接的文本,可以使用户从当前内容跳转到下一个内容
是一项协议,就像写信有格式一样,数据通过这种格式在客户端与服务端间传播
特点
- HTTP 和 HTTPS 的区别在于:HTTP为明文,HTTPS则进行了加密
HTTP的默认端口为 80
HTTPS的默认端口为 443
- 非持久连接:每次完成请求和响应都立刻断开客户端和服务端的连接
这样可以让服务器逐个处理多个请求,节省资源
HTTP1.1中为持久连接,客户端可连续发送多个请求
并使用Connection:close这一请求头来进行关闭
- 无状态:服务器不会记录历史请求数据
在例如用户登录的场景,为了防止用户反复登录(即保持登录状态)
可以用 cookie 这一请求头来实现状态管理
URL
URL:全称 Uniform Resource Locator (统一资源定位符)
顾名思义,URL就像地址一样,可以帮助用户到达指定的位置获取资源
URL结构
一个完整的URL结构由六个按顺序排列的部分构成:
<协议><域名><端口><路径><查询参数><锚点>
比如 https://www.example.com:8080/path?query=123#fragment
- 协议:https 比如http和https
- 域名:www.example.com 服务器的网络地址标识,用来找到对应到服务器
- 端口:8080 输入时浏览器会自动补全,可省略,用来连接服务器的指定服务
- 路径:/path 指定服务器的资源位置
- 查询参数:?query=123 向服务器传递额外信息,格式为"键=值"
- 锚点:#fragment 由客户端处理,定位到页面的具体位置
服务端与客户端
服务端:服务端是指运行在远程服务器上的程序或系统,主要负责处理客户端发送的请求并响应相应的数据或结果
客户端:客户端是用户这一端可以操作的程序,负责向服务器发送请求
请求和响应
请求
请求是由客户端发向服务端的
由 请求行 请求头 请求体 构成
请求行格式为 <请求方法> <请求URL> <版本>
请求和响应的<版本>不一定相同
请求方法最常见的是 GET POST GET是获取的方法 POST是传输提交数据的方法
GET 只获取数据,所以不含有请求体 POST 需要提交数据,可以含有请求体
GET 就像客户端告诉服务端,给我一个东西,客户端响应给客户端这个东西
POST 就像客户端告诉服务器,我要处理一个数据,客户端将数据处理后响应给客户端
将GET改成POST
将 GET 改为 POST
并添加 Content-Type: application/x-www-form-urlencoded 这个请求头
响应
响应是由服务端发向客户端的
由 响应行 响应头 响应体 构成
响应行格式为 <版本><状态码><原因短语>
状态码,顾名思义可以显示当前的状态,种类众多,且服务端可以自行创建状态码
以下是状态码的规律
- 1xx 表示请求已接收,继续处理 最常见的是 100
- 2xx 表示被正常处理 最常见的是 200
- 3xx 表示重定向 会让浏览器执行某些特殊的处理 最常见的是 304 表示可以使用缓存的内容
- 4xx 表示客户端错误 最常见的是 404 表示找不到请求的资源 403 表示请求被服务器拒绝
- 5xx 表示服务端错误 最常见的是 500 表示服务器内部发生故障
一个比较重要的响应头
Content-type(内容类型)
有了这个首部显示数据类型,浏览器就能更好的处理不同类型的数据(比如图片、视频、文本)
PHP
PHP格式
<?php
//执行相关的PHP代码
?>
变量赋值与计算
变量以 **\(** 开始,例如`\)a、$num`
变量名区分大小写
即y和Y是不同的变量
由于PHP是弱类型语言,所以不必声明变量的数据类型
比如
intfloat
| 运算符 | 等同于 | 描述 |
|---|---|---|
| x=y | x=y | 将y的值赋值给x |
| x?=y | x=x?y | "?"代表+、-、*、/ |
| 运算符 | 名称 | 描述 |
|---|---|---|
| x and/&& y | 和 | 若x和y都为ture,则返回ture |
x ory |
或 | 若x和y至少一个为ture,则返回ture |
x xor y |
异或 | 若x和y有且仅有一个为ture,则返回ture |
!x |
非 | 若x不为ture,则返回ture |
类型比较
- 松散比较:
==只比较值 - 严格比较:
===不仅比较值,还比较类型
输出
echo:可以输出一个及以上个字符串print:只允许输出一个字符串,返回值总为1
echo"Hello CTF";
数组
array()用于创建数组
$cars=array("Hello","CTF");
[]用于定义数组
$z = ['H','e','l', 'l', 'o'];
$z[0] = 'H';
$z[1] = 'r';
$z[2] = 'l';
$z[3] = 'l';
$z[4] = 'o';
魔术常量
__XXX__(如__FILE__)这样的预定义常变量,被称为魔术常量
__FILE__ //返回文件的完整路径和文件名
highlight_file(__FILE__); //代码高亮的显示当前文件内容
表单数据
-
\(_GET:接受 GET 请求传递的参数 示例:`example.com/index.php?book=HELLOCTF`你可以使用``\)_GET['book']``来获取相应的值
-
\(_POST:接受 POST 请求传递的参数 示例:`example.com/index.php?book=HELLOCTF`你可以使用``\)_GET['book']``来获取相应的值
-
$_REQUEST:接受
GET和POST以及Cookie请求传递的参数
文件操作函数
-
include():导入并执行指定的 PHP 文件
示例:include('config.php');会导入并执行config.php文件中的代码 -
require():类似于
include()但如果文件不存在,则会产生致命错误 -
include_once()、require_once(): 与
include和require类似,但只导入文件一次 -
fopen(): 打开一个文件或URL
示例:$file = fopen("test.txt", "r");即以只读模式打开test.txt -
file_get_contents():读取文件的全部内容到一个字符串
示例:$content = file_get_contents("test.txt"); -
file_put_contents():将一个字符串写入文件
示例:file_put_contents("test.txt", "Hello World!");
代码执行函数
-
eval():执行字符串中的 PHP 代码
示例:eval('$x = 5;');会设置变量$x的值为 5 -
assert():用于调试,检查一个条件是否为
true -
system()、shell_exec()、exec()、passthru():执行外部程序或系统命令
示例:system("ls");会执行ls命令并显示输出
反序列化函数
unserialize():将一个已序列化的字符串转换回 PHP 的值
示例:$array = unserialize($serializedStr);序列化的数组字符串→数组
数据库操作函数
mysql_query()、mysqli_query():发送一个 MySQL 查询
示例:$result = mysql_query("SELECT * FROM users");
其他函数
preg_replace():执行正则表达式搜索和替换
示例:$newStr = preg_replace("/apple/i", "orange", $str);会将$str中的"apple"替换为"orange"
create_function():创建匿名的 lambda 函数
示例:$func = create_function('$x', 'return $x + 1;');
RCE
RCE:全名Remote Code Execution(远程代码执行)
可以让攻击者直接向后台服务器远程注入操作系统命令或代码,从而控制后台系统
攻击原理及类型
RCE攻击通常通过网络应用程序和基础设施中的漏洞进行
常见的 RCE 漏洞类型:
-
注入漏洞:如 SQL注入 或 命令注入 ,通常由于输入验证不当导致
攻击者可以通过精心构造的恶意输入,使系统将这些输入解释为命令并执行 -
不安全的反序列化:反序列化是 将数据从一种格式转换为另一种格式 的过程
如果反序列化的数据结构不明确,攻击者可能会构造恶意输入,使系统在反序列化时误解数据,从而执行恶意代码 -
越界写入:缓冲区 是用于存储数据的固定大小的内存块
如果数据读写不安全,攻击者可能会将数据放置在被解释为代码或重要控制信息的位置 -
文件管理:某些应用程序允许用户上传文件
攻击者可以利用这一点 上传包含恶意代码的文件 ,并诱使应用程序执行这些代码
常见函数
用表格简单概括一下
| 函数 | 回显 | 参数数量 | 参数类型 |
|---|---|---|---|
| system() | √ | 2 | $command $return_var |
| exec() | X | 3 | $command $output $return_var |
| passthru() | √ | 2 | $command $return_var |
| shell_exec() | X | 1 | $command |
| `` | X | 1 | $command |
| popen() | X | 2 | $command $mand |
| proc_open() | X | 6 | $command $descriptor_spec $pipes $cwd $env_vars $options |
| pcntl_exec() | X | 3 | $path $args $envs |
- system()
有回显 最多两个参数
system(string $command,int &$return_var=?);
\(command:执行 *command* 参数所指定的命令并输出执行结果(如`ls` `pwd`) \)return_var:外部命令执行后的返回状态会设置到此变量中(执行成功为0 失败为1)
- exec()
基本无回显(只回显最后一行) 最多三个参数
可以用print_r();echo ;var_dump();等将$output中填充的数组回显出来
exec(string $command,array &$output=?,int &$return_var=?);
\(command:要执行的命令(如`ls` `pwd`) 单独使用时只有最后一行结果 无回显 \)output:用命令执行的输出填充此数组 每行输出填充数组中的一个元素 即逐行填充数组
$return_var:程序执行的状态(成功或失败/0或1)
- passthru()
有回显 最多两个参数
和system很像 但passthru可以输出二进制数据,发送到二进制数据流、web页面
passthru(string $command,int &$return_var=?);
\(command:同system \)return_var:同system
- shell_exec()
无回显 最多一个参数 若作为一个函数返回值为字符串
可以用print();echo ;来回显
shell_exec(string $command);
$command:要执行的命令(如
lspwd)
-
反引号
与shell_exec相同
`$cmd` -
popen()
无回显 两个参数
命令执行完后 需要用fgetsfread来获取其中的内容
然后才能用print_r();echo ;var_dump();回显出来
popen(string $command,string $mode);
\(command:要执行的命令(如`ls` `pwd`) \)made:模式(
r为阅读w为写入)
- proc_open()
无回显 最多六个参数 前三个参数是必要的
proc_open($command,$descriptor_spec,$pipes,$cwd,$env_vars,$options);
\(command:要执行的命令(如`ls` `pwd`) \)descriptor_spec:定义数组的内容
$pipes:调用数组的内容
- pcntl_exec()
无回显 三个参数
pcntl_exec(string $path,array $args=?,array $envs=?);
\(path:必须是可执行的二进制文件路径或在文件第一行指定了的一个可执行文件路径标头的脚本 比如文件第一行是`#!/usr/local/binn/perl` 就可以写perl \)args:要传递给程序的参数的字符串数组
$envs:要传递给程序作为环境变量的字符串数组
(key==>valuekey是要传递的环境变量的名称 value是环境变量的值)
WP
HTTP习题
Problem: [SWPUCTF 2024 秋季新生赛]http标头
思路
-
解题大致思路
-
1
使用Brup suite
将http发送到Repeater
发现显示"要在发售第一时间凌晨0点0时0分才能进下一关(注意date格式)" -
2
于是留意到响应中的DATE
多次发送,发现DATE随时间改变
于是觉得需要在请求中添加一个固定的DATE
留意date的格式:
Date: 星期几, 几日 几月 几年 时:分:秒 GMT
查阅得知,黑猴的发售时间是2024年8月20日星期二 -
3
所以在请求的HOST下填写:
Date: Tue, 20 Aug 2024 00:00:00 GMT
点击发送,发现新内容"玩黑神话当然要用BlackMonkey浏览器咯" -
4
提及到浏览器,于是想到更改请求中的浏览器
发现请求中的chrome
将chrome替换成:
BlackMonke
点击发送,发现新内容"悄悄告诉你悟空喜欢吃BlackMonkey牌的曲奇" -
5
曲奇的英语是cookie,正好是一种请求头
查询资料得知书写方法,在HOST下输入:
Cookie: cookie=BlackMonkey
点击发送,发现新内容"要从wukong那里过来的才能进入下一关" -
6
"过来"一词引发联想,联想到Referer
于是将Referer改为:
Referer: wukong
点击发送,发现新内容"你的ip不是本地回环地址,悟空不能轮回" -
7
提到ip和本地回环地址
说明需要修改ip地址
需要用到xff
本地回环地址为127.0.0.1于是输入:
X-Forwarded-For: 127.0.0.1
发现取得了flag -
FLAG
flag:NSSCTF{eaaf661c-e28d-4a20-b2e3-698223488a2f}
EXP
- 具体攻击代码
GET /wukong.php HTTP/1.1
Host: node6.anna.nssctf.cn:23359
Date: Tue, 20 Aug 2024 00:00:00 GMT
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlackMonkey/140.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: wukong
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: cookie=BlackMonkey
X-Forwarded-For: 127.0.0.1
总结
- 对该题的考点总结
- 修改DATE
- 修改请求中的浏览器
- 添加Cookie请求头
- 改变请求的来源Referer
- 通过xff更改请求中的ip地址

RCE习题
【RCE-labs】Level 0
进去发现是前言部分,简单介绍了一下RCE
「任意代码执行(Arbitrary Code Execution,ACE)」 是指攻击者在目标计算机或目标进程中运行攻击者选择的任何命令或代码的能力
这是一个广泛的概念,它涵盖了任何类型的代码运行过程,不仅包括系统层面的脚本或程序,也包括应用程序内部的函数或方法调用
在此基础上我们将通过网络触发任意代码执行的能力通常称为 远程代码执行 「远程代码执行(RCE,Remote Code Execution,RCE)」
「命令执行(Command Execution)」 通常指的是在操作系统层面上执行预定义的指令或脚本
如Windows中的CMD命令或Linux中的Shell命令,这在语言中可以体现为一些特定的函数或者方法调用,如PHP中的shell_exec()函数或Python中的os.system()函数
「代码执行(Code Execution)」 同我们最开始说到的任意代码执行,在语言中可以体现为一些函数或者方法调用,如PHP中的eval()函数或Python中的exec()函数
第一行直接给出了flag
flag{911b90f4ca424fedb59ab5fb000c7366}

【RCE-labs】Level 1
开篇介绍了代码执行及代码执行漏洞
当漏洞入口点可以执行任意代码时,我们称其为代码执行漏洞
这种漏洞包含了通过语言中对接系统命令的函数来执行系统命令的情况
比如eval("system('cat /etc/passwd')";);也被归为代码执行漏洞
其提到最常见的木马就用的eval()函数
并补充道 一般情况下,为了接收更长的Payload,一般对可控参数使用POST传参
发现有一行提示
try POST:
a=echo "Hello,World!";
所以得知需要使用POST,这里我使用了BP
发送到Repeater,在请求体输入a=echo "Hello,World!";
发现在响应里成功输出了Hello,World!
但是没有flag
所以应该这个提示只是帮忙上手的
重点应该在eval()上
所以先想办法看看里面有什么文件
输入a=eval('system("ls");'); 点击发送
发现 get_flag.php和index.php 两个文件
要取得flag,就选择查看get_flag.php
将请求改为a=eval('system("cat get_flag.php");');点击发送
发现内容为
$file_path = "/flag";
if (file_exists($file_path)) {
$flag = file_get_contents($file_path);
}
else{
$flag = "HelloCTF{Default_Flag}";
}
// 根目录下默认存有flag但不建议心存侥幸ww
简单读一下代码 应该是需要打开/flag
将请求改为a=eval('system("cat /flag");');点击发送
取得flag
flag{6423bc33ee7848d4a298b60105bf1016}

【RCE-labs】Level 2
开局提示为:
除开在一句话木马中最受欢迎用以直接执行PHP代码的 eval() 函数,PHP还有许多 回调函数 也可以直接或者间接的执行PHP代码
在该关卡中,你将会从能够执行代码的PHP函数中抽取一个,你需要填充函数的内容来执行某些代码以获取flag(tip:flag存储在 $flag 中,当然你也可以尝试其他方法)
通过发送GET ?action=r 的方式可以重置当前选中的函数 —— 或者你可以自己想办法可控它x
通过代码可以读出
得到函数后 需要用content来让程序执行Code内的命令
于是便用Hackbar发送一个GET
不用BP是因为试了两小时也没法随机出除了
assert的值
不知道咋回事 然后用assert也没法成功POST
就换Hackbar做了
加上一个?action=r发送
一直刷到返回eval
主要刷到
eval界面就固定了 刷得简单一些
改为POST
将action=r改为action=submit
在请求体中加入content=system("echo $flag")
点击EXECUTE 取得flag
flag{bfff8f6a081a42dfb369e7091575a4d9}

【RCE-labs】Level 3
提示为
当漏洞入口点只能执行系统命令时,我们可以称该漏洞为命令执行漏洞,如下面修改过的 "一句话木马"
try POST:
a=cat /etc/passwd;
通过读代码可知 需要使用POST 对a进行处理
使用Hackbar发送POST 请求体内容为a=ls/
借此查看根目录下的文件 发现有个文件叫flag
将请求体改为a=cat /flag点击EXECUTE
取得flag
flag{5d8a269ebb8849dd85ff3ae17f1dfa58}

【RCE-labs】Level 4
打开有个链接https://www.runoob.com/linux/linux-shell-basic-operators.html
进去发现是介绍Shell 基本运算符
提示:
try GET:
?ip=8.8.8.8
flag is /flag
题中还提供了一些
&&(逻辑与运算符): 只有当第一个命令 cmd_1 执行成功(返回值为 0)时,才会执行第二个命令 cmd_2。例: mkdir test && cd test
||(逻辑或运算符): 只有当第一个命令 cmd_1 执行失败(返回值不为 0)时,才会执行第二个命令 cmd_2。例: cd nonexistent_directory || echo "Directory not found"
&(后台运行符): 将命令 cmd_1 放到后台执行,Shell 立即执行 cmd_2,两个命令并行执行。例: sleep 10 & echo "This will run immediately."
;(命令分隔符): 无论前一个命令 cmd_1 是否成功,都会执行下一个命令 cmd_2。例: echo "Hello" ; echo "World"
根据经验可知 这是用来绕过的
通过读代码 得知我们需要GET一个ip
先按提示走 用Hackbar发出GET请求 添加?ip=8.8.8.8 得到
PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=42 time=191.630 ms --- 8.8.8.8 ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 191.630/191.630/191.630 ms
通过读代码 大致得知 只有GET到一个ip的值 才能执行hello_server
再加上题上说flag is /flag 大致知道需要对这个ip修改
将请求改出?ip=8.8.8.8;cat /flag
这样可以在执行ip赋值的同时 连带着把查看flag的命令一起执行
点击EXECUTE 取得flag
flag{42551c7c7c46491f91a4b1e7ed1fa4b2}

【RCE-labs】Level 5
提示为
在Shell中,单/双引号 "/' 可以用来定义一个空字符串或保护包含空格或特殊字符的字符串
例:echo "$"a 会输出 $a,而 echo $a 会输出变量a的值,当只有""则表示空字符串,Shell会忽略它
| 符号 | 作用 | 例子 |
|---|---|---|
*(星号) |
匹配零个或多个字符 | *.txt |
?(问号) |
匹配单个字符 | file?.txt |
[](方括号) |
匹配方括号内的任意一个字符 | file[1-3].txt |
[^](取反方括号) |
匹配不在方括号内的字符 | file[^a-c].txt |
{}(大括号) |
匹配大括号内的任意一个字符串 | file{1,2,3}.txt |
提到“通过组合上述技巧,我们可以用于绕过CTF中一些简单的过滤”
system("c''at /e't'c/pass?d");
system("/???/?at /e't'c/pass?d");
system("/???/?at /e't'c/*ss*");
...
通过读代码 了解到
需要用到 GET 获取cmd
如果直接打开 flag 会显示 WAF
所以我们需要上方提供的技巧来绕过这个识别
通过 Hackbar 发送一个GET请求
在URL末尾加上?cmd=cat /fl""ag
点击EXECUTE 取得flag
flag{cc2471b7f3324c55b5f6da92c4235cb1}

【RCE-labs】Level 6
映入眼帘的是“刚才,学了什么来着!?”
了解到大概需要上一关学到的知识来作答
通过读代码得知需要GET一个cmd
如果有[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]
会显示 WAF
所以只有 a 这个字母可用
没禁 ?
?可以匹配单个字符
所以可以用 a 和 ? 来操作
输入?cmd=/???/?a?/??a?
正常来说应该是cmd=/bin/cat/flag
结果返回个ZmxhZ3s4OGJlN2Y5ZDBiZWU0NjZmYTBkOGYwZDAxMDg1M2VjOX0K
不清楚咋回事
尝试第二种思路
这个是没思路了看的别人WP
用Linux八进制来绕过
$`xxx`
xxx表示八进制形式
输入?cmd=$'\143\141\164' $'\057\146\154\141\147'
对应的是?cmd=cat /flag
点击EXECUTE 取得flag
flag{88be7f9d0bee466fa0d8f0d010853ec9}

【RCE-labs】Level 7
提示为
在遇到空格被过滤的情况下,通常使用 %09 也就是TAB的URL编码来绕过,在终端环境下 空格 被视为一个命令分隔符,本质上由 $IFS 变量控制,而 $IFS 的默认值是空格、制表符和换行符,所以我们还可以通过直接键入 $IFS 来绕过空格过滤
通过读代码可知
需要GET一个cmd
/flag| 都被禁了
所以一方面我们要绕过/flag
一方面要绕过cat和/flag间的空格
输入?cmd=cat%09/fl""ag
点击EXECUTE 取得flag
flag{df0ef6d5a40145bfbda85983295cf876}

【RCE-labs】Level 8
提示为
大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端 —— 这些是命令有回显的基础。
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
$ command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
$ command > /dev/null 2>&1
通过读代码得知
cmd的内容会被扔到/dev/null
所以我们需要令后半段语句没法执行
联想到第四题的知识点
||(逻辑或运算符): 只有当第一个命令 cmd_1 执行失败(返回值不为 0)时,才会执行第二个命令 cmd_2。例: cd nonexistent_directory || echo "Directory not found"
所以输入?cmd=cat /flag||
这样写 为cmd赋值完成后 后半段的写入/dev/null的命令不会被执行
点击EXECUTE 取得flag
flag{e62abcc44b4041068477b7805f1cae85}


浙公网安备 33010602011771号