Echo战队第16次培训——CDN原理、ARP欺骗、奈菲解锁原理1

CDN原理 我们先来看一下正常上网的拓补图 图中是一个正常的网站请求,这个应该不难理解 但不知道你有没有注意到一个问题,如果这个网站是一个被架设在大洋彼岸的网站,那么网站的内容要想从地球的另一端来到你的电脑,得有多费劲? 所以聪明的人类就发明出了一种十分暴力但却有效的方法来解决这一世纪难题,也就是将网站的内容先缓存到一台离你的电脑比较近的服务器上存起来,等你要用的时候直接调用这台服务器上的内容就行。而这种技术,就被叫做CDN存储技术(Content Delivery Network) 这样的方法看着简单,但却十分有效。不过也有一个致命硬伤,就是你没法保证你所访问内容的时效性。因为听名字就知道,缓存,就是暂时存储的意思,那就必然会有时间差的存在。而这个时间差的大小也就取决于网站管理者,不过一般来说这个时间差和网站访问速度是成反比的 不过对于科学上网而言,内容储存并不是我们关注的重点。重点是CDN服务所提供的域名。对于一个可以高速裸奔的科学上网的节点,其最大的痛处想必就是稳定性没法得到保证。因此,想办法提升稳定性就变成了非安全节点的关键。而一个节点之所以会被长城防火墙干趴,本质上就是节点ip或域名被拉了黑名单。 而CDN的存在就误打误撞地解决了这个问题。 当防火墙在探测你的节点ip时,碰到的确实CDN的域名。而CDN本身只起到了挡箭牌的作用,就算一个CDN域名被拉黑了,再把你的节点服务器重新再部署一个就行了。而且整个过程都是免费的(如使用cloudlare),这对科学上网来说简直就算一大神器。 但CDN也不是完全没有缺点。CDN确实能够保障你的节点安全,但速度却大打折扣。从上面的原理图里应该可以看出CDN服务器其实本身就是一个挂在墙外的东西,即便你的节点本身速度够快,但被CDN这么一折腾,也还是会有很大程度上的降速。因此,给你的节点套CDN作为一个备选方案,才算是一个不错的选择 ARP欺骗 ARP(Address Resolution Protocol),中文解释为地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议。 ARP大致工作流程如下: 主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。 收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。 地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存(重点来了,我们要利用的就是这个点!) 攻击者可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。 那么 我们如何利用ARP协议来进行断网攻击呢?大致思路如下: 首先,ARP是建立在网络中各个主机互相信任的基础上的,它的诞生使得网络能够更加高效的运行,但其本身也存在缺陷: ARP地址转换表(ARP缓存)是依赖于计算机中高速缓冲存储器动态更新的,而高速缓冲存储器的更新是受到更新周期的限制的,只保存最近使用的地址的映射关系表项,这使得攻击者有了可乘之机,可以在高速缓冲存储器更新表项之前修改地址转换表,实现攻击。ARP请求为广播形式发送的,网络上的主机可以自主发送ARP应答消息,并且当其他主机收到应答报文时不会检测该报文的真实性就将其记录在本地的MAC地址转换表,这样攻击者就可以向目标主机发送伪ARP应答报文,从而篡改本地的MAC地址表。这就是所谓的APR欺骗攻击。 ARP欺骗可以导致目标计算机与网关通信失败,更会导致通信重定向,所有的数据都会通过攻击者的机器,因此存在极大的安全隐患,这里我们是使目标主机通信失败,之后的教程会介绍重定向的方法。 ARP攻击大致流程如下: 第一步:假设主机A访问某网站,那么要告知某网站我的IP和MAC地址,但这是以广播的方式告知的。 第二步,由于是广播方式告知,猥琐的B主机也知道了A的IP和Mac地址,于是猥琐的B主机把自己伪装成某网站,给A发送回复,A误以为B就是某网站,因此更新了自己本机的ARP缓存。 第三步,两者建立连接,A不断将自己的请求发给B,可怜的某网站被挂在一遍了。如果B转发A的请求给某网站,再转发给A,那相当于A的流量都从B经过,这就是步骤4,因此可以会话劫持;如果B给A的是一个假地址,那么可怜的A永远上不了网了。

March 30, 2023 · 1 min · Red

Echo战队培训番外——CSRF伪造银行转账请求漏洞复现

“欺骗的最高境界,就是不说任何一句假话” 昨天和实验室的朋友在聊天时提到了一些典型的漏洞案例,我用白话向他们介绍了一些攻击手段,不乏ARP欺骗、metaploit病毒制作、常规抓包等,其中“访问一些不该访问的页面就能把你银行卡里的钱转走”好像让他们比较提起兴趣,正好之前给Echo培训的时候也演示过,反映还不错。下面我尽量用所有0基础者都能听懂的话,再复现一遍整个过程 下面来介绍一下实验环境 环境 受害者:windows 7,chrome浏览器 攻击者:windows 11(黄色网站运营者) 银行:windows 7,Mysql 5.7数据库 背景 有三个银行账户,账户ID、密码、原始金额如下 实验开始 (实验确保严格按照下面步骤进行,无多余操作) 某天康康想给杰瑞转1000块钱,于是就正常地打开了银行的网站并登录,看到了自己账户的余额和转账的界面 出于平时“网上冲浪”的经验,对于1000块的巨款,康康谨慎地打开了所有杀毒软件,在确保了“全盘扫描”全部正常并绿灯通行后,才输入了杰瑞银行账号并进行了转账 杰瑞此时也收到了这1000块钱,这一切似乎都很安全并且正常,至少目前为止是这样的 转完帐后闲来无事的康康,打算看点网站来保障青少年的身心健康,于是他“安全退出”了银行账户后,便进入了下面这个网站 激动的康康决定马上接受美女荷官的邀请,于是他便点进了链接 在经过一番荷官的心理辅导后,康康看了一眼自己的手机,发现手机短信似乎多了一条来自银行的短信。嗯?1000块转账?怎么转给杰瑞的钱到现在才提醒 不信邪的康康决定打开账户一探究竟,但让他不解的是,为什么银行卡里的钱又少了1000? 不行,一定是我眼花了,嗯,一定是辅导还不到位,再看一遍就好了 机智的康康再次刷新了荷官的页面 又过了一段时间,康康再次返回银行账户界面,7000??wtf?? 康康急忙打电话给杰瑞,发现杰瑞的账户里只收到了最开始的1000块 这期间究竟发生了什么呢?让我们切换一下视角,从银行的数据库这个“上帝视角”来一探究竟 你一定已经发现了康康的2000去了哪,但是这期间究竟发生了什么呢? 原理分析 我们先来看一下CSRF漏洞的wiki定义 其实简单来说就是以利用受害者本身被银行信任的设备请发起正常的转账请求,但显然CSRF漏洞的应用远不止这一种 我们先来看一下正常银行转账页面发起转账请求的代码 <?php include("config.php"); if(isset($_COOKIE["userid1"]) && isset($_COOKIE["pass1"])) { $TOID=$_GET['nameid']; $TOMONEY= $_GET['balance']; include("conn/conn.php"); $sql=$db->query("select * from account where userid='".$TOID."'"); $sqlres = $sql->fetch(); if($sqlres['userid'] != $TOID) { echo "<script language=\"javascript\">"; echo "alert('用户名或密码错误!')"; echo "return(true)"; echo "location.href=\"default.php\""; echo "</script>"; } $balance_new = $TOMONEY + $sqlres['balance']; $sql=$db->exec("update account set balance='".$balance_new."' where userid='".$TOID."'"); $sql=$db->query("select * from account where userid='".$_COOKIE['userid1']."'"); $sqlres = $sql->fetch(); $balance_new = $sqlres['balance']- $TOMONEY; $sql=$db->exec("update account set balance='".$balance_new."' where userid='".$_COOKIE['userid1']."'"); echo "<script language=\"javascript\">"; echo "location.href=\"default.php\""; echo "</script>"; } ?> 可以看到整个过程其实很好理解,就是调用银行本身数据库进行数据增减 接下来我们再看看荷官页面的代码,相信你就知道问题出在哪里了 ...

March 24, 2023 · 1 min · Red

[XUJCOJ] 2023天梯赛校内选拔赛

记录部分题目 8:常规赛排名 描述 一场常规赛需要参赛双方进行至多三局游戏来决出胜者(先赢两局者胜)。 赢得一场常规赛可以为战队获取 1 个场次分,输掉一场常规赛则无事发生。 同时,每赢得一局游戏可以为战队获取 1 个游戏分,输掉一局游戏则会失去 1 个游戏分。 例如 TeamA 和 TeamB 的游戏结果是 2:1,那么本场比赛结束后,TeamA 获得 1 个场次分和 1 个游戏分,TeamB 只失去 1 个游戏分。 具体排名规则如下: 优先按照场次分高低排名,场次分相同的,则按照游戏分从高到低排名,仍然相同的,则按照战队名称的字典序从小到大排。 输入 第一行是一个正整数 n 表示总共的常规赛场次。(1 ≤ n ≤ 105) 接下来 n 行,每行包含两个字符串和一个比分,表示参赛双方的战况。 战队名称仅包含大小写字母且长度不超过 10,比分只会是 0:2、1:2、2:1、2:0 中的一个。 输出 按排名规则的顺序输出若干行,每行包括:战队名 场次分 游戏分。 样例输入 3 TeamA TeamB 2:1 TeamB TeamC 2:0 TeamA TeamC 1:2 样例输出 TeamB 1 1 TeamA 1 0 TeamC 1 -1 思路 这是一道模拟题,需要模拟比赛过程并统计每个队伍的分数。我们可以使用一个unordered_map来存储每个队伍的信息,使用一个结构体Team来表示每个队伍的名称、场次分和游戏分。对于每一场比赛,我们可以解析输入并根据比分更新每个队伍的信息。如果某个队伍赢了比赛,它的场次分和游戏分都会增加;如果它输了比赛,则它的游戏分会减少。 一旦我们统计了每个队伍的分数,我们可以将它们放入一个vector中,并使用一个compare函数对它们进行排序,以便根据场次分、游戏分和队伍名称的字典序输出排名。最后,我们遍历排好序的vector并输出每个队伍的信息。 总之,这道题的思路比较简单,重点在于正确解析输入并更新每个队伍的信息。我们需要考虑所有可能的比分情况,并使用一个unordered_map来快速查找每个队伍的信息。 代码 #include <iostream> #include <vector> #include <string> #include <unordered_map> #include <algorithm> #include <sstream> using namespace std; struct Team { string name; int matches_won; int game_points; }; bool compare(const Team& a, const Team& b) { if (a.matches_won == b.matches_won) { if (a.game_points == b.game_points) { return a.name < b.name; } return a.game_points > b.game_points; } return a.matches_won > b.matches_won; } int main() { int n; cin >> n; cin.ignore(); unordered_map<string, Team> teams; for (int i = 0; i < n; ++i) { string line; getline(cin, line); istringstream iss(line); string team1, team2; char delimiter; int score1, score2; iss >> team1 >> team2 >> score1 >> delimiter >> score2; if (score1 > score2) { teams[team1].matches_won++; teams[team1].game_points += (score1 - score2); teams[team2].game_points -= (score1 - score2); } else { teams[team2].matches_won++; teams[team2].game_points += (score2 - score1); teams[team1].game_points -= (score2 - score1); } teams[team1].name = team1; teams[team2].name = team2; } vector<Team> team_list; for (const auto& team : teams) { team_list.push_back(team.second); } sort(team_list.begin(), team_list.end(), compare); for (const auto& team : team_list) { cout << team.name << " " << team.matches_won << " " << team.game_points << endl; } return 0; } 9:炉石传说-沉没之城 描述 小卢在玩某款卡牌游戏,每个回合中,他可以执行以下三种操作任意次。 ...

March 18, 2023 · 4 min · Red

[重返NOIP] 模拟——NOIP2006 提高组 作业调度方案

题目描述 我们现在要利用 m 台机器加工 n 个工件,每个工件都有 m 道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。 每个工件的每个工序称为一个操作,我们用记号 j-k 表示一个操作,其中 j 为 1 到 n 中的某个数字,为工件号; k 为 1 到 m 中的某个数字,为工序号,例如 2-4 表示第 2 个工件第 4 道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。 例如,当 n=3,m=2 时,1-1,1-2,2-1,3-1,3-2,2-2 就是一个给定的安排顺序,即先安排第 1 个工件的第 1 个工序,再安排第 1 个工件的第 2 个工序,然后再安排第 2 个工件的第 1 个工序,等等。 一方面,每个操作的安排都要满足以下的两个约束条件。 对同一个工件,每道工序必须在它前面的工序完成后才能开始; 同一时刻每一台机器至多只能加工一个工件。 另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。 由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为 1 1 2 3 3 2。 还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。 例如,取 n=3,m=2 ,已知数据如下(机器号/加工时间): 工件号 工序 1 工序 2 1 1/3 2/2 2 1/2 2/5 3 2/2 1/4 则对于安排顺序 1 1 2 3 3 2,下图中的两个实施方案都是正确的。但所需要的总时间分别是 10 与 12 。 ...

March 15, 2023 · 2 min · Red

[issue]关于近期发现洛谷判题错误的求证

不知道有没有人遇到和我一样的问题 近日突然想起来洛谷部分题目存在判断失误问题(包括之前遇到过的一些题已经忘记题号),以下面两道题为例,贴出我的得分情况、输出情况和代码(已确认无空格和换行等差异) P1067 我的代码 #include<stdio.h> int main(){ int n,t; scanf("%d",&n); t=n; while(n--){ int a; scanf("%d",&a); if(n!=0){ if(a>0&&(a!=1&&a!=-1)){ if(t==n+1) printf("%dx^%d",a,n+1); else printf("+%dx^%d",a,n+1); }else if(a==-1||a==1){ if(a>0&&t==n+1) printf("x^%d",n+1); else if(a>0&&t!=n+1) printf("+x^%d",n+1); else printf("-x^%d",n+1); }else if(a<0&&(a!=1&&a!=-1)){ printf("%dx^%d",a,n+1); }else{ t--; } } else{ if(a>0&&(a!=1&&a!=-1)){ if(t==n+1) printf("%dx",a); else printf("+%dx",a); }else if(a==-1||a==1){ if(a>0&&t==n+1) printf("x"); else if(a>0&&t!=n+1) printf("+x"); else printf("-x"); }else if(a<0&&(a!=1&&a!=-1)){ printf("%dx",a); }else{ t--; } } } int a; scanf("%d",&a); if(a>0&&t!=0) printf("+"); printf("%d\n",a); } AC代码 #include <bits/stdc++.h> using namespace std; int main() { int n,a,i; cin>>n; for(i=n;i>=0;i--) { cin>>a; if(a) { if(i!=n&&a>0) cout<<"+"; if(abs(a)>1||i==0) cout<<a; if(a==-1&&i) cout<<"-"; if(i>1) cout<<"x^"<<i; if(i==1) cout<<"x"; } } return 0; } P1098 这道题只下载了第7个测试点 ...

March 13, 2023 · 2 min · Red