hxp 36C3 CTF Web题 WriteupBin Writeup (Selenium模拟点击+Content Security Policy+Nonce+Parsley.js触发错误提示)

本文是关于hxp 36C3 CTF Web挑战WriteupBin的详细分析,涉及Selenium模拟点击、Content Security Policy(CSP)、Nonce和Parsley.js错误提示的利用。作者通过分析源码、CSP策略和前端验证库Parsley,揭示了解题的关键在于利用错误提示的位置变化,模拟admin用户点赞行为,逐步猜测并获取admin的Writeup ID,最终揭示flag。
摘要由CSDN通过智能技术生成

WriteupBin

– A web challenge from hxp 36C3 CTF

https://ctftime.org/event/825

题目部署

本地搭建:

解压WriteupBin.tar.xz,在Dockerfile所在目录下执行:

echo 'hxp{FLAG}' > flag.txt && < /dev/urandom tr -dc a-f0-9 | head -c 16 > writeup-id.txt && docker build -t writeupbin . && docker run --cap-add=SYS_ADMIN --security-opt apparmor=unconfined -ti -p 8001:80 writeupbin

访问127.0.0.1:8001

​ 这道题整个分析过程比题目本身更重要,所以我不会像普通的Writeup一样像个直通车每一步都走得特到位直抵flag,而是像走迷宫一样迂回式前进,每走一步都停下来分析,如果碰壁也要分析碰壁的原因。

题目分析

这道题基于这样一个发布和显示Writeup的平台。页面最上面可以浏览当前用户发布的WP;页面中间0f0e打头的这个字符串是当前session的用户id(并非PHPSESSID);下面的输入框可以写wp,点击submit提交后会跳转到show.php页面。

在这里插入图片描述

每一个wp都分配一个id,比如这里的73fd8aefbbc2768c,这个id值和get参数id的值是对应的,都是相同的16位hex。show.php里面显示出了wp的内容,当前用户可以点赞,还可以把wp展示给Admin用户。

好像光看这些不知道从何下手。

我们手头还有源码包。

这个题目的源码压缩包应该是作为题目的附件直接提供给做题者的,所以先来瞅一眼压缩包里能给我们什么样的提示。

.
├── Dockerfile							//Docker文件
├── admin.py								//使用selenium模拟admin登录并点赞
├── db.sql									//数据库文件
├── docker-stuff
│   ├── default							//配置文件
│   └── www.conf						//配置文件
├── www
│   ├── general.php					//连接数据库设置header头等一些初始化操作
│   ├── html
│   │   ├── add.php					//添加writeup相关操作
│   │   ├── admin.php				//把writeup提交给admin
│   │   ├── index.php				//入口文件
│   │   ├── like.php				//点赞操作
│   │   ├── login_admin.php	//admin登陆操作
│   │   └── show.php				//获取writeup内容
│   └── views
│       ├── header.php			//在页面上方展示目前id提交的writeup
│       ├── home.php				//页面中部用来提供给用户输入的界面
│       └── show.php				//点赞、提交给admin的展示页面
└── ynetd										//用来启动 admin.py

有一堆php,还有一个.py文件,一个Dockerfile,一个.sql的数据库文件等等。

我们先来看看题目是怎么部署的,也就是看看Dockerfile文件里有什么名堂。

COPY db.sql writeup-id.txt flag.txt /root/

可以看到flag文件是先从源码的根目录复制到了docker里的root目录下,

RUN replace '__FLAG__' "$(cat /root/flag.txt)" -- /root/db.sql

然后flag.txt里面的内容又被写到了root目录下db.sql这个数据库文件里,flag的真实值替换掉了数据库文件里flag的占位符__FLAG__

一同被写入db.sql的还有writeup_ID、数据库密码等等。

replace '__DB_PASSWORD__' "$(< /dev/urandom tr -dc A-Za-z0-9 | head -c32)" -- /root/db.sql /var/www/general.php && \

replace '__WRITEUP_ID__' "$(cat /root/writeup-id.txt)" -- /root/db.sql /var/www/html/admin.php && \

< /dev/urandom tr -dc A-Za-z0-9 | head -c32 > /root/admin-token.txt && \
replace '__ADMIN_TOKEN__' "$(cat /root/admin-token.txt)" -- /home/ctf/admin.py && \

replace '__ADMIN_HASH__' "$(php -r 'echo password_hash($argv[1], PASSWORD_DEFAULT);' -- $(cat /root/admin-token.txt))" -- /var/www/html/login_admin.php

再来看db.sql是如何处理这些写入的数据的:

值得关注的语句如下:

db.sql

USE `writeupbin`;
INSERT INTO `writeup` (id, user_id, content) VALUES ('__WRITEUP_ID__','admin','__FLAG__');

相当于Writeup_ID的值、“admin”、还有flag的值分别插入到了writeupbin数据库下writeup表中id、user_id、content这三个数据项下。

id user_id content
__WRITEUP_ID__的值 admin __FLAG__的值

顺着这个思维继续往前走,数据库里面的记录是如何被网页调用的呢?

我们来到 /var/www/html/show.php

$stmt = $db->prepare('SELECT id, content FROM `writeup` WHERE `id` = ?');
$stmt->bind_param('s', $_GET['id']); //防止SQL注入
$stmt->execute();
$writeup = mysqli_fetch_all($stmt->get_result(), MYSQLI_ASSOC)[0];

可以看到,show.php通过get请求参数‘id’获取到id号(这个id就是前面提到的每个wp的编号),然后把id的16位hex值代入sql查询语句,将writeup表的相关数据取出来存到$writeup变量里。

show.php底部包含了 …/views/show.php 这个文件

include('../views/show.php');

而$writeup变量就是在这里被调用的

<?= $writeup['content'] ?>,在/views/show.php页面里将id对应的content显示出来。

这下就明了了:拿flag的方法,就是输入admin的Writeup ID(唯一)作为show.php的get参数提交,这样从数据库取出的content就是flag的值,会在show.php页面里显示出来。可以这么理解:admin用户唯一的那个writeup的内容就是flag值。

但是怎么获取到admin的writeup id呢?

先说句题外话:对于数据库writeup表中非admin用户的记录,id和content两个字段存放的其实就是我们在index界面输入框提交的wp的编号和内容,user_id存放的是session id。

id user_id content
writeup的id $_SESSION[‘id’] writeup的内容

这个从add.php里可以体现出来:

$stmt = $db->prepare('INSERT INTO `writeup` (id, user_id, content) VALUES (?,?,?)');
$id = id();
$stmt->bind_param('sss', $id, $_SESSION['id'], $_POST['content']);
$stmt->execute();

总结一下:

Writeup数据表

写入数据库方式 用户 id(数据项) user_id(数据项) content(数据项)
docker部署时写入 admin用户 __WRITEUP_ID__的值(我们的 目标) admin FLAG
网页输入框提交 非admin 用户1(session1) Writeup 1-1的id (16位hex) $_SESSION[‘id’] Session 1 用户id (16位hex) Writeup 1-1的内容
网页输入框提交 非admin用户1(session1) Writeup 1-2的id (16位hex) $_SESSION[‘id’] Session 1 用户id (16位hex) Writeup 1-2的内容
非admin用户1(session1) Writeup 1-n的id (16位hex) Writeup 1-n的内容
网页输入框提交 非admin用户2(session2) Writeup 2-1的id (16位hex) $_SESSION[‘id’] Session 2 用户id (16位hex) Writeup 2-1的内容
网页输入框提交 非admin用户n(session n) Writeup n-1的id (16位hex) $_SESSION[‘id’] Session n 用户id (16位hex) Writeup n-1的内容

我们把目光重新聚焦到如何获取admin的id上来。

很容易想到的一个想法就是,index页面上会显示出当前session用户所撰写的所有wp的id,点进去就是一个个wp,如果我们把当前session的用户id改成admin,那么岂不是就能显示出admin的writeup id了吗?

这种可能性应该是没有的,要不然这个题目就太简单了。。。

保险起见还是分析一下。

我们看一下general.php,Session id就是在这里生成的。

function id() {
   
    return bin2hex(random_bytes(8));
}
...
if( ! isset($_SESSION['id'])) {
   
    $_SESSION =</
### 回答1: 二维TE波FDTD方法的收敛性通常可以通过计算误差来验证。一种常用的方法是使用与解析解比较的误差,但是由于解析解通常难以得到,因此我们可以使用网格收敛性分析方法。 具体的,我们可以固定物理模型和计算区域大小,然后逐步减小网格大小进行模拟,并计算出每个网格大小下的误差。通常情况下,我们期望误差随着网格大小的减小而减小,且其减小速度应该接近于一个常数,即网格收敛阶。 在二维TE波FDTD方法中,我们可以引入高斯源来进行模拟,并计算出其收敛阶。下面是一个基于Matlab实现的示例代码: ```matlab % 二维TE波FDTD方法引入高斯源作图,显示收敛阶matlab实现 clc; clear; close all; %% 配置模拟参数 c0 = 3e8; % 光速 dx = 1e-3; % 网格步长 dy = dx; dt = dx/c0/sqrt(2); % 时间步长 T = 4e-9; % 计算时间 Lx = 20e-3; % 计算区域长度 Ly = Lx; x = (-Lx/2:dx:Lx/2); % 网格点坐标 y = (-Ly/2:dy:Ly/2); nx = length(x); ny = length(y); t = (0:dt:T); % 时间坐标 nt = length(t); z0 = 377; % 自由空间阻抗 eps0 = 8.854e-12; % 真空介电常数 mu0 = pi*4e-7; % 真空磁导率 epsx = ones(nx, ny)*eps0; % x方向介电常数 epsy = ones(nx, ny)*eps0; % y方向介电常数 mux = ones(nx, ny)*mu0; % x方向磁导率 muy = ones(nx, ny)*mu0; % y方向磁导率 %% 定义高斯源及其位置 x0 = 0; y0 = 0; % 高斯源位置 s = 1e-10; % 高斯源时间宽度 f = 1e9; % 高斯源中心频率 A = 1; % 高斯源幅值 t0 = 3*s; % 计算的起始时间 h = waitbar(0, '正在计算中,请稍等...'); Ez = zeros(nx, ny); for n = 1:nt waitbar(n/nt, h, sprintf('已完成 %.2f%%', n/nt*100)); % 更新Ez场 Hy = Hy - dt./muy.*diff(Ez, [], 1)/dx; Hx = Hx + dt./mux.*diff(Ez, [], 2)/dy; Ez(:, 2:end-1) = Ez(:, 2:end-1) + dt./(epsx(:, 2:end-1).*dy)./muy(:, 2:end-1).*(Hx(:, 2:end-1)-Hx(:, 1:end-2)) - ... dt./(epsy(2:end-1, :).*dx)./mux(2:end-1, :).*(Hy(2:end-1, :)-Hy(1:end-2, :)); % 更新高斯源 Ez(round(nx/2)+round(x0/dx), round(ny/2)+round(y0/dy)) = A*exp(-((n*dt-t0)/s)^2)*cos(2*pi*f*(n*dt-t0)); end %% 计算收敛阶 err = zeros(4, 1); for p = 1:4 nxp = nx*2^(p-1); nyp = ny*2^(p-1); dxp = Lx/nxp; dyp = Ly/nyp; dtp = dxp/c0/sqrt(2); x = (-Lx/2:dxp:Lx/2); y = (-Ly/2:dyp:Ly/2); Ezp = zeros(nxp, nyp); for n = 1:nt % 更新Ez场 Hyp = Hy - dtp./muy.*diff(Ezp, [], 1)/dxp; Hxp = Hx + dtp./mux.*diff(Ezp, [], 2)/dyp; Ezp(:, 2:end-1) = Ezp(:, 2:end-1) + dtp./(epsx(:, 2:end-1).*dyp)./muy(:, 2:end-1).*(Hxp(:, 2:end-1)-Hxp(:, 1:end-2)) - ... dtp./(epsy(2:end-1, :).*dxp)./mux(2:end-1, :).*(Hyp(2:end-1, :)-Hyp(1:end-2, :)); % 更新高斯源 Ezp(round(nxp/2)+round(x0/dxp), round(nyp/2)+round(y0/dyp)) = A*exp(-((n*dtp-t0)/s)^2)*cos(2*pi*f*(n*dtp-t0)); end err(p) = norm(Ez(1:2^(p-1):end, 1:2^(p-1):end)-Ezp, 'fro'); end %% 绘制误差随网格大小变化的图像 figure; loglog([1, 2, 4, 8], err, '-o'); xlabel('网格大小比例'); ylabel('误差'); grid on; p = polyfit(log([1, 2, 4, 8]), log(err), 1); fprintf('计算收敛阶为: %.2f\n', -p(1)); ``` 在上述代码中,我们首先定义了模拟参数,然后引入高斯源进行模拟,计算出Ez场。接着,我们使用不同的网格大小分别进行模拟,并计算误差。最后,我们绘制了误差随网格大小变化的图像,并使用拟合方法计算出了收敛阶。 这里我们使用了二维TE波FDTD方法,如果你需要使用其他方法进行模拟,可能需要对代码进行相应的修改。 ### 回答2: 在使用二维时域有限差分时间域方法(FDTD)时,要引入高斯源来进行计算。二维TE波源可以通过一个电磁脉冲来模拟,其中电场沿着一个方向振荡,磁场沿着垂直的方向振荡。我们可以使用Matlab来实现这个模拟,并绘制出收敛阶曲线。 首先,我们需要创建一个二维网格来表示空间中的点。我们可以选择一个合适的分辨率,然后在每个网格点上存储电场和磁场的数值。对于TE波源,我们只需要在一个位置上设置一个高斯波包。 然后,我们需要在每个时间步长上更新电磁场的数值。根据FDTD的离散方程,在每个时间步长上,我们可以使用电磁场在邻近点的数值来更新当前点的数值。这个过程可以通过一个循环来实现。 接下来,我们可以选择一些合适的观测点,计算它们上的电磁场的数值,并将其保存下来。通过将这些数值与理论解进行比较,我们可以计算出FDTD方法的收敛阶。 最后,我们可以使用Matlab中的绘图函数来绘制收敛阶的曲线。我们可以将网格分辨率作为横坐标,收敛阶作为纵坐标。在每个网格点上,我们可以计算出收敛阶,并绘制出整个曲线。 总之,通过引入高斯源并使用Matlab实现二维TE波的FDTD方法,我们可以绘制出收敛阶曲线,以评估FDTD模拟的准确度和收敛性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值