夏令营讲稿

第一节课——PHP基础&特性

前言

首先给大家推荐一个快速入门任何一门编程语言的平台https://www.runoob.com/

想学好网络安全,打好ctf的话,最基本的编程功底是很重要的,虽然我们是不需要像开发一样那样可以运用语言去做项目,但是我们也是必须了解的,最起码的要求就是要能够读懂代码,知道这个代码的运行逻辑是什么,知道这个代码做了什么,这样在进行web渗透的时候,才能有相应的打法去做。

好那么我接下来要讲一个在web这个方向常见的语言——php,它被经常用来开发网页,且相对于java语言来说要相对的简单易学一些,更适合新手入门。

首先php文件他的常见文件名后缀就是.php

我们要知道php之所以被大量运用在网页的开发上,其中一个很重要的原因是php文件可以包含文本、HTML、JavaScript代码这些在网页开发中必须使用到的语言,它的兼容性是很好的。PHP代码在服务器上执行后,结果会以纯HTML形式返回给浏览器

那么PHP能做什么呢?

  • PHP 可以生成动态页面内容
  • PHP 可以创建、打开、读取、写入、关闭服务器上的文件
  • PHP 可以收集表单数据
  • PHP 可以发送和接收 cookies
  • PHP 可以添加、删除、修改您的数据库中的数据
  • PHP 可以限制用户访问您的网站上的一些页面
  • PHP 可以加密数据

现在再给大家推荐一个可以在线运行php代码的网站https://www.bejson.com/runcode/php/

我们知道任何一个编程语言都是要先在电脑上安装相应的环境才可以使用的,但是直接用网站他在网站就直接内置了环境,所以更加方便了新手,如果大家有兴趣可以去CSDN搜索一下如何配置php环境,也不是很难,如果要更深入的学习php语言的话,安装环境是必须的,当然我前期为了让大家快速上手,就直接用网站代替了。

还有我们要明确,我们最终的目的还是要运用到web的学习上去,所以我们初期学习php语言时,不需要像学习开发一样事无巨细,什么细节都学,这样的话你自己的正反馈其实是很少的,也很难坚持下去,所以后面例如注释的语法这些对web学习没有较大影响的知识我就先略过不表,只讲一些最关键的点。但是这不代表php学到这个程度就够了,我讲的这点东西是远远远远不够的,借用我之前做过的一道CTF题里面的彩蛋来说就是:

语法

  • 变量以 $ 符号开始,后面跟着变量的名称
  • 变量名必须以字母或者下划线字符开始
  • 变量名只能包含字母、数字以及下划线(A-z、0-9 和 _ )
  • 变量名不能包含空格
  • 变量名是区分大小写的($y 和 $Y 是两个不同的变量)

格式

PHP脚本是以**<?php**开始,以**?>**结束

好下面就是学习编程语言的保留环节——输出helloworld。在php语言中输出helloword也是非常简单的,这个代码量感觉仅次于python了。

<?php
echo "Hello World!";
?>

这就是php输出helloworld的代码,这里的echo就相当于python中的print,当然php也可以用print,但是你们会遇到最常见的肯定还是echo,也就是打印输出你后面写的字符内容

注意:PHP中的每个代码行都必须以分号结束。分号是一种分隔符,用于把指令区分开来,这里和c是比较像的,由于python实在是太简单了,连这步都省了哈哈哈。所以其实单纯编程角度来说的话,我觉得最适合入门的就是python,它能帮助你快速理解编程到底是个什么东西,能快速的入门编程,但是的话在web这个方向毕竟还是php的应用更加普遍,当然到了后期学习的时候,在ctf的很多题目也需要你来编写脚本来解题,这里的脚本有很多都是用python语言编写的,因为它本身的易用性,还有python丰富的库,可以让你相对于别的语言来说更加轻松地去解决问题。编程总归是每个学习计算机方向的人的内功,所以总的来说还是多多益善。

变量

变量是用于存储信息的“容器”:

这里给大家看一个具体例子:

<?php
$x = 5;
$y = 6;
$z = $x + $y;
echo $z;
?>

我们可以看到输出结果是11,这就是最简单的赋值运算,这里的美元符号就是在php中赋值的格式,意思就是将x赋值为5,将y赋值为6,然后将x+y的结果赋值给z,然后再输出z的值,很简单,加一加最后结果就是11。当然我们刚刚讲到的最基本的php格式php头,php尾都是不可缺少的。

PHP变量规则:

  • 变量以 $ 符号开始,后面跟着变量的名称
  • 变量名必须以字母或者下划线字符开始
  • 变量名只能包含字母、数字以及下划线(A-z、0-9 和 _ )
  • 变量名不能包含空格
  • 变量名是区分大小写的($y 和 $Y 是两个不同的变量)

这个看看就行了,实际当你碰到一串源码的时候,你只要知道这个是变量就行。

PHP是一门弱类型语言

在上面的例子中,我们注意到,不必向PHP表名该变量的数据类型,这就是和像C这种强类型的编程语言不一样的地方,C中我们要声明一个变量,譬如说声明一个整数我们就要加个int。

PHP数据类型

PHP变量存储不同的类型的数据,不同的数据类型可以做不一样的事情。
PHP支持以下几种数据类型:

  • String(字符串)
  • Integer(整型)
  • Float(浮点型)
  • Boolean(布尔型)
  • Array(数组)
  • Object(对象)
  • NULL(空值)
  • Resource(资源类型)(不常见)

PHP字符串

字符串就是一串字符的序列,例如”Hello World!”
你可以将任何的文本放在单引号和双引号中

PHP整型

整数就是一个没有小数的数字,他有三种格式:十进制,十六进制,八进制

-345 0x8c 047

PHP浮点型

浮点型就是带小数的数字

PHP布尔型

布尔型就两种,TRUE或者FALSE

PHP数组

数组在一个变量中存储多个值

$cars=array(“Volvo”,”BMW”,”Toyota”);

PHP对象

对象数据类型也可以用于存储数据,在PHP中,对象必须声明,关键字是class

<?php
class Car
{
  var $color;
  function __construct($color="green") {
    $this->color = $color;
  }
  function what_color() {
    return $this->color;
  }
}
?>

PHP类型比较

虽然PHP是弱类型语言,但也需要明白变量类型以及它们的意义,因为我们经常 要对PHP变量进行比较,包含松散和严格比较。

松散比较:使用两个等号==比较,只比较值,不比较类型。
严格比较:用三个等号===比较,除了比较值,也比较类型。

例如:”24”是一个字符串而24是一个整数。FALSE是一个布尔值而”FALSE”就是一个字符串,这两个例子就是他们的值是相等的,但是类型是不等的。我们可以实操来看一下结果

<?php
if(42 == "42") {
    echo '1、值相等';
}

echo PHP_EOL; // 换行符

if(42 === "42") {
    echo '2、类型相等';
} else {
    echo '3、类型不相等';
}
?>
输出结果:
1、值相等
3、类型不相等

这里的if语句就是做一个逻辑判断的,当if括号里的值为真时,就会执行花括号里面的内容,当为假就不执行,如果有else就会执行else花括号里面的内容。

通过这个实例我们就看出42和”42”这里的值就是相等的,但是两者类型是不相等的,一个是整型,一个是字符串。提前预告一下,这里就涉及到后面的CTF知识点了,也就是php的弱类型比较绕过。

最最基本的php内容就先讲到这,其实你前面C语言的课好好听的话,其实编程语言之间都是通的,所以我就不再赘述了,我觉得如果你前面C掌握的不错的话,这点内容对你们来说就是小菜一碟。

Web

get&post请求

下面就开始讲一些和web密切相关的东西了,首先讲一下get和post请求

譬如说这里的网址栏上有个?s=php,这就是get请求的标准格式,我们可以在network一栏里直接刷新一下,点击数据包就可以看到这是一个GET请求

而另一种请求方式post请求他表面看是看不到的,比如说你在网站上登录账号的时候一般就是用的post请求,我们同样可以在network那看到请求方式

那么在很多ctf题目中,他需要我们去进行get和post的请求,get请求很简单,就直接在地址栏输入就行了,那么post我们该怎么请求呢?

这里用两种办法,一种办法是使用burpsuite这个渗透测试软件,还有一种办法更加简单,你可以在浏览器下载一个hackbar插件,这样就可以直接在浏览器发送post请求了,当然get请求也可以更加清晰的进行发送。

==与=== (弱类型比较)

<?php
header("Content-Type:text/html;charset=utf-8");
$flag='dozer nb!';
$a=1;
if($a==$_GET['x']){
    echo $flag;
}

这里我们的目标就是要让他打印出flag,这个源码的基本逻辑就是当$a与你用get方式传入的参数弱相等时,他就会输出flag

当x=1或x=1.0或x=+1或x=1admin时,都会正确的输出flag,因为两个等于号,他就只会比较值,而不会比较类型,而前面几种写法,他们的值都无一例外,都是1,所以自然if判断就是真,就直接执行输出flag的命令了。所以我们要明白一点,当使用“==”时,进行对比会有缺陷,不是非要1才能相等,甚至“1a”也可以。这种判断是不具有唯一性的,会有多个字符使条件成立。

<?php
header("Content-Type:text/html;charset=utf-8");
$flag='dozer nb!';
$a=1;
if($a===$_GET['y']){
    echo $flag;
}

那如果是三个等于号呢?那就只能输入1了,当输入别的值的时候就直接gg,因为三个等于号在判断值的同时还会判断数据类型,譬如说1admin就是字符串类型,和原本的1整型是不一样的。

下面我就来讲一些php特定的函数特性所造成的绕过

md5

<?php
header("Content-Type:text/html;charset=utf-8");
$flag='dozer nb!';
if($_GET['name']) != $_GET['password']){
    if(MD5($_GET['name']) == MD5($_GET['password'])){
        echo $flag;
    }
     echo '?';
}

这里的代码逻辑是什么呢?就是用get请求方式传入两个参数,一个叫name,一个叫password,这两个参数值不可以相等,但是经过md5加密后,两者的md5值需要相等,这样才会把flag输出给你。这看起来似乎是有些矛盾的,其实这里要用到的绕过知识就是0e绕过

0e绕过本质上用到的就是刚刚讲到的php的弱类型比较的特性和科学计数法的运用。科学计数法就是一种记数的方法。计算器中表达10的幂一般是用e来表示。比如说2760000这个数字他就等于2.76*10^6=2.76e6,所以0e是什么意思,就是无论后面跟什么值,都是0乘以10的几次方,所以无论后面跟什么值,他都等于0,因此就达到了绕过的目的。

<?php
header("Content-Type:text/html;charset=utf-8");
$flag='dozer nb!';
if($_GET['name']) != $_GET['password']){
    if(MD5($_GET['name']) === MD5($_GET['password'])){
        echo $flag;
    }
     echo '?';
}

如果是三个等于号,就需要用到数组绕过了

因为PHP是弱类型语言,所以有时会根据引用变量时所处的环境,将变量自动转换为最适合的类型。在进行“==”判断的时候,会先将字符串类型转化成相同再比较。转换的规则为,若该字符串以合法的数值开始,则使用该值,否则其值为0。但是在三个等于号时,他就不会将0e值转换为0了,这个时候就只有数组绕过管用了。

md5计算的是一个字符串的哈希值,传入数组时会直接报错,但会继续执行并且返回结果为null,所以在进行强比较时,此时两边的值都是null

而他的数据类型就是NULL,所以值和数据类型都是相等的,也就成功绕过了。

这里给大家一篇文章,就是讲关于md5绕过的相关姿势的:

https://blog.csdn.net/LYJ20010728/article/details/116779357?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171803858416800180687639%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171803858416800180687639&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-116779357-null-null.142

这里总结了很多md5绕过姿势,比如说一些字符串经过md5加密后是0e开头的,这样就可以很轻松的通过0e绕过

intval

相关链接:https://www.runoob.com/php/php-intval-function.html

intval函数用于获取变量的整数值,intval函数通过使用指定的进制base转换(默认是10进制),返回变量var的integer数值,也就是整型,intval不能用于object也就是对象类型,否则会产生错误

$flag='dozer nb!';
$i='666';
$ii=$_GET['n'];
if(intval($ii==$i)){
    echo $flag;
}

当给ii赋值为+666时,会自动将+666转换为10进制的整数即666,与i就是相等

譬如说加上0x就会使用16进制转化,666的16进制是0x29a

strpos

相关链接https://www.runoob.com/php/func-string-strpos.html

查找 “php” 在字符串中第一次出现的位置:

比如这个例子就是查找php字符串第一次出现的位置,空格也占一个位置,然后众所周知计算机从0开始数,就能轻松数出7了。这里强调一下,往往后面的题目强调这个函数找相关字符串的功能性,而这个字符串究竟在哪个位置,反而显得不那么重要了,现在说可能有点抽象,待会给你们看一道CTF题目你们就知道了

in_array

相关链接https://www.runoob.com/php/func-array-in-array.html

在数组中搜索值 “Runoob” ,并输出一些文本:

$flag='dozer nb!';

$whitelist = [1,2,3];
$page=$_GET['i'];
if (in_array($page, $whitelist)) {
    echo $flag;
}

这是第三个参数,如果没有设置,就相当于是”==”缺陷,默认是不会去检测类型
再来看源码,这里就存在绕过了,如果输入?i=1e也是会输出flag的,如果第三个参数没有设置,就可以绕过

preg_match

相关链接https://www.runoob.com/php/php-preg_match.html

preg_match 函数用于执行一个正则表达式匹配。

$flag='dozer nb!';
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/",$num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

进行数组绕过,就可以让preg_match函数失效,直接进行下一个if语句的判断,达到绕过的目的,这个函数在ctf实战中是比较常见的,用数组绕过就行,我前面也是讲过了。

str_replace

相关链接https://www.runoob.com/php/func-string-str-replace.html

$sql=$_GET['s'];
$sql=str_replace('select',$sql);
echo $sql;

就是把select去掉的意思
简单的过滤sql语句,存在缺陷就是无法迭代,我们可以输入sselectelect进行绕过,这个函数只能过滤一次,这种绕过方式叫做双写绕过

CTF题目——php代码审计

preg_match

include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/,$num")){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

直接数组绕过,?num[]=1即可得到flag,数组绕过的主要目的就是为了让preg_match这个函数失效

intval-进制转换

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

直接进制转换一下,将4476转换为16进制即0x117c即可

preg_match-换行绕过

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im',$a)){
        if(preg_match('/^php/i',$a)){
            echo 'hacker';
        }
        else{
            echo $flag;
        }
}
else{
        echo 'nonononono';
}

:”^”这个符号代表着前面有没有字符都无所谓,只要有php这个字符就行。
/i 表示不区分大小写(如果表达式里面有a,那么A也是匹配对象)
/m表示可以进行换行

换行绕过,%0a表示换行,输入%0aphp即可得到flag

intval-化整绕过

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

输入?num=4476.1即可,intval会将4476.1转换成整数即4476,但是如果输入4476.0在第一步进行比较的时候与4476是弱相等的,所以第一步是绕不过去的

intvla-化整绕过(2)

include("flag.php"):
highlight_file(__FILE__);
if(isset($_GET['num'])){
        $num = $_GET['num'];
        if($num==4476){
                die("no no no!");
        }
        if(preg_match("/[a-z]/i,$num")){
                die("no no no!");
        }
        if(intval($num,0)==4476){
                echo $flag;
        }else{
                echo intval($num,0);
        }
}

strpos

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
        $num = $_GET['num'];
        if($num==="4476"){
                die("no no no!");
        }
        if(preg_match("/[a-z]/i,$num")){
                die("no no no!");
        }
        if(!strpos($num, "0")){
                die("no no no!");
        }
        if(intval($num,0)===4476){
                echo $flag;
        }
}

这里就不能用4476.1来绕过了,因为最后一步是”===”,他会严格比较大小,所以这里应该用进制转化+换行绕过,输入?num=%20010574,其中%20相当于空格也就是换行了,010574是4476的8进制。

注:还可以输入4476.0也可以成功绕过

strpos(2)

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
        $num = $_GET['num'];
        if($num=="4476"){
                die("no no no!");
        }
        if(preg_match("/[a-z]/\./i,$num"){
                die("no no no!");
        }
        if(!strpos($num, "0")){
                die("no no no!");
        }
        if(intval($num,0)===4476){
                echo $flag;
        }
}

同样输入?num=%20010574即可绕过,因为这题他会过滤了”.”,所以4476.0是不可以使用了。

==——弱类型比较

highlight_file(__FILE__);

if(isset($_GET['u'])){
        if($_GET['u']=='flag.php'){
                die("no no no");
        }else{
                highlight_file($_GET['u']);
        }
}

既要读出flag.php同时又得通过弱类型比较,可以加上路径,输入?u=./flag.php,即可得到flag

  1. Unix/Linux 命令行: 当你在Unix或Linux系统的命令行中看到”./“时,它通常表示当前目录。例如,运行一个程序时,如果你使用”./program”,那么它意味着在当前目录下运行名为”program”的程序。
  2. 文件路径: 在文件路径中,”./“可以表示相对于当前工作目录的路径。

md5

include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a'])) and isset($_POST['b'])) {
if ($_POST['a']) != $_POST['b'])
if (md5($_POST['a'])) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}

post输入a和b,md5数组绕过一下就行了,a[]=1&b[]=2

作业

请大家在NSSCTF平台上注册一个账号,然后做一下这道题——[SWPUCTF 2021 新生赛]easy_md5,NSSCTF平台是一个CTF的刷题平台,这上面有很多CTF以往比赛的题目,如果大家有兴趣的话可以看标题上有新生赛字眼的,一般就是比较简单的题目,是比较适合大家入门的,希望大家能对CTF产生兴趣,然后加入我们的集训队吧。

https://www.nssctf.cn/user/login?redirect=/index

这题至少有两种解法可以做出来噢,我在课上也是讲了两种方法的,如果大家感兴趣的可以两种方法都做一下。

备案

CTF中常见的Linux命令

burp安装

攻防世界

simple_php

command_execution

如果直接传入a=0,那么$a就是false,也就是假,这个条件判断就通不过了

ping 127.0.0.1;find / -name “flag*”

好用的工具网站:https://xuhuhu.com/#escape

PHP2

信息搜集

url编码的本质是”%”+ascii码的十六进制

%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65

NSSCTF

Ping——禁用js

XYCTF

zzl的护理小课堂

BUUCTF

[BJDCTF 2020]easy_md5

万能密码

ffifdyop

[UUCTF 2022 新生赛]websign

前端的js把一些常见的快捷键都禁用完了,所以我们可以直接抓包绕过前端的限制

第二节课——CTF中的Web&实战做题

CTF中的Web实战题——简单

信息搜集

NSS

[SWPUCTF 2021 新生赛]gift_F12

[第五空间 2021]WebFTP

[NSSCTF 2022 Spring Recruit]ezgame

BUU

[第一章 web入门]常见的搜集

[第一章 web入门]粗心的小李

代码托管平台:github,这上面有非常多好用的工具,大家如果要好好学习计算机专业的,这个代码平台是必须要知道并且熟练知道该怎么去使用的。

http

XYCTF

题目链接:https://gz.imxbt.cn/games/4/challenges

像http这一类的题目,他一般都会综合我刚刚讲的一个知识点,也就是信息搜集,像先看这里,我们查看一下源码,就获得到了提示——为了防止忘记密码,我把它们放在某个地方了。那会放在什么地方呢,我上面也讲过了,一般常见的路径就是robots.txt,如果这里没有的话,我们可以查看一下network里加载的一些数据包,这里有时候出题人会给你一些备注,可能也是会有提示在里面的,如果一些常见的地方都没有的话,那我们就可以直接上工具dirsearch来扫描一下常见目录,基本上就可以大差不差的找到题目的关键信息了。

我们在robots.txt路径里发现了下一个路径,也就是/10g1n.txt

我们访问一下这个路径

就成功得到了开头我们看到的那个登录框所需要的用户名和密码,我们可以拿记事本来给他记录一下,然后在那个登录框里输入一下用户名和密码,我们就进入到了下一步

他这里说什么不是yuanshen.com来的我不要,这里就是出题人在考你一些常见的http请求头你认不认识了,这里贴一篇文章,里面就很好的总结了一些常见的http的知识,当然这个yuanshen肯定就是出题人的夹带私货了。

CTF中常用的http知识点总结

回到题目上来,这里的从哪里哪里来其实就是在暗示你要加一个Referer的请求头,这个Referer的请求头的意思就是先前网页的网址,当前请求网页紧随其后,即来路,也就是说你是从yuanshen.com看到这个网站的链接的,然后你在原神的网站上点了链接,在数据包中就会多一个请求头叫Referer,那么我们在做这道题的时候就是要去伪造这个请求头,让网站后端处理数据的时候认为你是从yuanshen.com来的,这样你才可以通过验证,进入下一个步骤。那么我们该如何做到呢?这里我们可以用两个工具,分别是hackbar和burpsuite,那我在这里就演示一下hackbar是怎么来操作的,感兴趣的兄弟可以用burpsuite也去尝试一下,原理是一样的。

我们调出hackbar,然后加载一下这个网站的url,然后添加以下http请求头即可,这里我们添加Referer请求头叫做yuanshen.com,然后点击Execute执行,我们就可以成功通过这一步进入下一步