考察知识点:
1.python基本编程;
2.远程linux服务器目录/tmp的利用;
3.二分查找算法
解题过程:
a.按照提示,nc pwnable.kr 9007连接服务器,服务器返回游戏玩法提示信息,即根据N(总数量)和C(测试次数)寻找一堆球里面一个重量jiao小的球(重量为9,其余重量为10),时间限制为30秒;
b.显然,使用二分法查找,函数结构为迭代,利用pwntools编程测试;
c.发现从远程终端运行exp,查找过程比较慢(这个情况程序在玩法提示信息中也有说明),可以利用linux系统的/tmp目录本地执行exp;题目未给远程账户,因此,使用其他题目提供的账户如:col@pwnable.kr,使用下面的命令将exp上传到服务器/tmp目录:
scp -P 2222 coin_exp.py col@pwnable.kr:/tmp
d.上传后通过col账户登录系统(ssh col@pwnable.kr -p2222),进入/tmp 目录,输入ls查看上传文件,发现没有权限,不影响, 直接运行python coin_exp.py,得到flag。
代码如下:
from pwn import *
global c #每次游戏允许猜的次数
global t #每次游戏目标球的编号,从0开始
def check(x,y):
#x:区间的开始编号
#y:区间的末尾编号
global c
global t
c=c+1
print "checking "+str(x)+","+str(y)
if(x==y):
#当当前区间只剩一个球,一定是目标球
t=x
return x
if(c==C-1):
#提示这是本轮最后一次猜测,编程习惯,没什么用
print "No more chances!"
P=(x+y)/2
#区间中点
s=""
#s:区间左半部分
r=0
#r:保存作伴部分球的总重量
for i in range(x , P+1):
s=s+str(i)+" "
print s
R=(P-x+1)*10
#R:如果目标球不在左半部分,左半部分的重量应该是这些
p.sendline(s)
r=int(p.recv())
#显示左半部分真实重量
print "res="+str(r)
print "R="+str(R)
if(R!=r):
#如果目标球在左半部分
check(x,P)
else:
#如果目标球不在左半部分
check(P+1,y)
if __name__ == "__main__":
p=remote('pwnable.kr','9007')
print p.recv()
time.sleep(3)
while(1):
inf = p.recv()
print inf
N=int(inf.split(' ')[0].split('=')[1])
C=int(inf.split(' ')[1].split('=')[1])
#print "N="+str(N)
#print "C="+str(C)
c=0
t=-1
check(0,N-1)
print "target="+str(t)
#print "send t=:"+str(t)
p.sendline(str(t))
u=p.recv()
#print "recev:"+u
if(len(u)<10):
#因为系统在收到目标球时,不会直接返回correct,而是返回重量9,再返回Correct!因此,在只剩两球情况中,当目标球是第一个球时,check会接受返回的重量9,而当目标球是b时,在check中,因为只剩一个球直接返回结果b,所以需要接收一次9,再发送目标球编号,再接收字符串 "Correct!(n)"
#print "t="+str(t)
#print "send t=:"+str(t)
p.sendline(str(t))
u=p.recv()
print "recv:"+u
#print p.recvuntil("N=")
#print "recved:"+p.recv()
inf=""