Description
I’ve got another task for you involving my little language, Prop!
It consists of the following expressions (denoted
e):
- Boolean constants, written
trueandfalse.- Boolean variables, written as any alphabetical string.
- Implies, written
e -> e.- Equivalence, written
e <-> e.- Negation, written
!e.- And, written
e && e.- Or, written
e || e.You can also parenthesize any expressions or subexpressions (so, you can write
(a || b) && c). Here’s another example program:(a || b) && c && d && (!d || b) || (b -> c) && (d <-> a)This time, I’ve determined that the attached program is satisfiable, but I want to know the actual values that make this program satisfiable. Can you find me a satisfying model, please? When you’ve found one, submit a GET request to our server. Any missing keys are considered false.
Here’s an example request:
http://problems3.2016q1.sctf.io:11420/?a=true&b=true&c=falseThis request would treat
aandbastrueandcand the rest of the variables asfalse. Thus, it’s only necessary to assign the true keys to the value true.
Resolution
At first glance, we can say that this language looks a lot like python.
Well, okay, maybe that just looks like any language, but what can i say …
So our idea was to transform the datas to a true python statement, and then bruteforce the given values to make the statement be True
We start loading our ‘prop’ program as a string data :
f=open('program.prop','rb'
datas=f.read()
f.close()
Then we transform that text to actual text that a python interpreter might understand. for the most part this is pretty straigtforward :
datas=datas.replace('&&', 'and')
datas=datas.replace('||', 'or')
datas=datas.replace('<->', '==')
datas=datas.replace('true','True')
datas=datas.replace('false','False')
datas=datas.replace('!','not ')
Implies doesn’t really exist in Python, so this one was a little bit trickier.
A -> B means that if you have A, then you should also have B. so either you don’t have A (A=False) or you have A=True and B=True
this could be simplified by “(not A) or B”
but we now can’t “just” make a simple remplace, as A can be a complexe part, because of all those parenthesis.
so we have to iterate threw all of the datas :
#A implies B == (not A) or B
while '->' in datas:
index=datas.index('->')
datas=datas[:index]+'or'+datas[index+2:]
#search for the beggening of A
parent_count=0
in_var_name=False
for index2 in range(len(datas[:index][::-1])):
if datas[:index][::-1][index2]=='(':
if parent_count==0 and in_var_name==True :
#found the start
datas=datas[:index-(index2)]+'not '+datas[index-(index2):]
break
parent_count-=1
if parent_count==0 :
#found the start
datas=datas[:index-(index2+1)]+'not '+datas[index-(index2+1):]
break
elif datas[:index][::-1][index2]==')':
if in_var_name==True and parent_count==0 :
#found the start
datas=datas[:index-(index2)]+'not '+datas[index-(index2):]
break
parent_count+=1
elif datas[:index][::-1][index2]==' ':
if in_var_name==True and parent_count==0 :
#found the start
datas=datas[:index-(index2+1)]+'not '+datas[index-(index2+1):]
break
else :
in_var_name=True
we have a second problem with the !/not,
>> False == not( True) File "&lt;input&gt;", line 1 False == not( True) ^ SyntaxError: invalid syntax
but this is okay :
>> False == (not( True)) True
so we have to re-parenthesis our nots:
</pre>
<pre>lastindex=0
while datas.find('not',lastindex)>0:
index=datas.find('not',lastindex)
lastindex=index+4
print lastindex
datas=datas[:index]+'('+datas[index:]
#search for the begening of A
parent_count=0
in_var_name=False
for index2 in range(len(datas[index+4:])):
if datas[index+4:][index2]=='(':
parent_count+=1
elif datas[index+4:][index2]==')':
if parent_count==0 :
#found the start
datas=datas[:index+4+(index2)]+') '+datas[index+4+(index2):]
break
parent_count-=1
if parent_count==0 :
#found the start
datas=datas[:index+4+(index2+1)]+') '+datas[index+4+(index2+1):]
break
elif datas[index+4:][index2]==' ':
if in_var_name==True and parent_count==0 :
#found the start
datas=datas[:index+4+(index2+1)]+') '+datas[index+4+(index2+1):]
break
else :
if datas[index+4:][index2:index2+3]!='not' and datas[index+4:][index2-1:index2+3-1]!='not' and datas[index+4:][index2-2:index2+3-2]!='not':
in_var_name=True
#print datas
f=open('raw','wb')
f.write(datas)
f.close()
print 'Done'
return 1
We should now have an acceptable Python expression that could be rendered by our interpreter
this can easily be done threw the eval() function that will take a string expression as an input .
Finally, we just have to bruteforce our “Expression” to get correct values so it will be evaluated as True.
this can be done quite quickly with a recursive algorithm :
<pre>def test(var=[],index=0,value=True):#var containt all our variables (a,b,c,d...z)
var[index]=value
if index==25:#once we have changed all the values, we can evaluate
if the_test(var) ==True:
print "Found one solution!"
for i,v in enumerate(var):
print "%s=%s&"%(alphabet2[i],v),
return True
else : return False
else : #As long as we are not at the 'z' variable, we keep going
if test(var,index+1,True)==True: #next variable in the list should be either True
return True
else :
if test(var,index+1,False)==True: #Or False
return True
return False</pre>
Good work! The flag is sctf{TOO_sat_FOR_you}.
[sCTF 2016] [CODE 130 – I Can’t Get No Satisfaction] Write Up