Google Chrome < M72 - PaymentRequest Service Use-After-Free

EDB-ID:

46472

CVE:

N/A




Platform:

Multiple

Date:

2019-03-01


There are several object-lifetime issues in the browser process in the 
implementation of payments.mojom.PaymentRequest.

The PaymentRequest object contains a std::unique_ptr to a 
PaymentRequestSpec, which is initialised during the call to PaymentRequest::Init. 
(https://cs.chromium.org/chromium/src/components/payments/content/payment_request.cc?rcl=7ba58abc11b141bafe03e017c5e027b889782223&l=114)

If we call PaymentRequest::Show on an initialised PaymentRequest, then we will pass this PaymentRequestSpec pointer to a new PaymentRequestSheetController.
(https://cs.chromium.org/chromium/src/chrome/browser/ui/views/payments/payment_request_dialog_view.cc?rcl=cededb4b546c5082ef1a207b67d9744481c4aa8d&l=415)

It will be stored as a raw pointer there with the comment

"// All these are not owned. Will outlive this."

(https://cs.chromium.org/chromium/src/chrome/browser/ui/views/payments/payment_request_sheet_controller.h?rcl=cededb4b546c5082ef1a207b67d9744481c4aa8d&l=168)

The comment, however, is incorrect, and there is no guarantee that the spec_ pointer will still be valid when the PaymentRequestSheetController later uses it. If the client makes a second call to PaymentRequest::Init, then the spec_ object will be free'd immediately.

Note that the same appears to be true of the state_ object, which is also passed in to the PaymentRequestSheetController.

To reproduce you need a local build of chrome; run the attached script 

$ python ./copy_mojo_js_bindings.py /path/to/chrome/.../out/Asan/gen
$ python -m SimpleHTTPServer&
$ out/Asan/chrome --enable-blink-features=MojoJS --user-data-dir=/tmp/nonexist 'http://localhost:8000/index.html'

Then once the chrome window is open and the renderer tabs have started, close the running chrome (ctrl-c will work fine) and you will likely see the issue. It may take several tries for everything to line up right.

=================================================================
==157886==ERROR: AddressSanitizer: heap-use-after-free on address 0x6140003c8d70 at pc 0x5645e19a8acd bp 0x7ffd25a60110 sp 0x7ffd25a60108
READ of size 8 at 0x6140003c8d70 thread T0 (chrome)
    #0 0x5645e19a8acc in begin buildtools/third_party/libc++/trunk/include/vector:1506:30
    #1 0x5645e19a8acc in RemoveObserver base/observer_list.h:282
    #2 0x5645e19a8acc in payments::PaymentRequestSpec::RemoveObserver(payments::PaymentRequestSpec::Observer*) components/payments/content/payment_request_spec.cc:211
    #3 0x5645e138787c in ~PaymentSheetViewController chrome/browser/ui/views/payments/payment_sheet_view_controller.cc:383:11
    #4 0x5645e138787c in payments::PaymentSheetViewController::~PaymentSheetViewController() chrome/browser/ui/views/payments/payment_sheet_view_controller.cc:382
    #5 0x5645e1338d5d in operator() buildtools/third_party/libc++/trunk/include/memory:2325:5
    #6 0x5645e1338d5d in reset buildtools/third_party/libc++/trunk/include/memory:2638
    #7 0x5645e1338d5d in ~unique_ptr buildtools/third_party/libc++/trunk/include/memory:2592
    #8 0x5645e1338d5d in ~pair buildtools/third_party/libc++/trunk/include/utility:315
    #9 0x5645e1338d5d in __destroy<std::__1::pair<views::View *const, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > > > buildtools/third_party/libc++/trunk/include/memory:1734
    #10 0x5645e1338d5d in destroy<std::__1::pair<views::View *const, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > > > buildtools/third_party/libc++/trunk/include/memory:1597
    #11 0x5645e1338d5d in std::__1::__tree<std::__1::__value_type<views::View*, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > >, std::__1::__map_value_compare<views::View*, std::__1::__value_type<views::View*, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > >, std::__1::less<views::View*>, true>, std::__1::allocator<std::__1::__value_type<views::View*, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > > > >::destroy(std::__1::__tree_node<std::__1::__value_type<views::View*, std::__1::unique_ptr<payments::PaymentRequestSheetController, std::__1::default_delete<payments::PaymentRequestSheetController> > >, void*>*) buildtools/third_party/libc++/trunk/include/__tree:1863
    #12 0x5645e13328be in clear buildtools/third_party/libc++/trunk/include/__tree:1900:5
    #13 0x5645e13328be in clear buildtools/third_party/libc++/trunk/include/map:1274
    #14 0x5645e13328be in payments::PaymentRequestDialogView::Cancel() chrome/browser/ui/views/payments/payment_request_dialog_view.cc:123
    #15 0x5645de3ae330 in views::DialogClientView::CanClose() ui/views/window/dialog_client_view.cc:119:52
    #16 0x5645de375084 in views::Widget::Close() ui/views/widget/widget.cc:580:46
    #17 0x5645d8940eff in payments::ChromePaymentRequestDelegate::CloseDialog() chrome/browser/payments/chrome_payment_request_delegate.cc:77:20
    #18 0x5645e198d77d in payments::PaymentRequest::OnConnectionTerminated() components/payments/content/payment_request.cc:434:14
    #19 0x5645e198ffda in payments::PaymentRequest::Show(bool) components/payments/content/payment_request.cc
    #20 0x5645d18f3921 in payments::mojom::PaymentRequestStubDispatch::Accept(payments::mojom::PaymentRequest*, mojo::Message*) gen/third_party/blink/public/mojom/payments/payment_request.mojom.cc:1562:13
    #21 0x5645d902900e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:423:32
    #22 0x5645d903b1bd in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:869:42
    #23 0x5645d90398c7 in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:590:38
    #24 0x5645d9024b25 in mojo::Connector::ReadSingleMessage(unsigned int*) mojo/public/cpp/bindings/lib/connector.cc:476:51
    #25 0x5645d9026308 in mojo::Connector::ReadAllAvailableMessages() mojo/public/cpp/bindings/lib/connector.cc:505:10
    #26 0x5645d9077881 in Run base/callback.h:129:12
    #27 0x5645d9077881 in mojo::SimpleWatcher::OnHandleReady(int, unsigned int, mojo::HandleSignalsState const&) mojo/public/cpp/system/simple_watcher.cc:273
    #28 0x5645d8d13d51 in Run base/callback.h:99:12
    #29 0x5645d8d13d51 in base::debug::TaskAnnotator::RunTask(char const*, base::PendingTask*) base/debug/task_annotator.cc:99
    #30 0x5645d8d11525 in base::MessageLoopImpl::RunTask(base::PendingTask*) base/message_loop/message_loop_impl.cc:374:46
    #31 0x5645d8d127e9 in DeferOrRunPendingTask base/message_loop/message_loop_impl.cc:385:5
    #32 0x5645d8d127e9 in base::MessageLoopImpl::DoWork() base/message_loop/message_loop_impl.cc:473
    #33 0x5645d8d1a5e6 in HandleDispatch base/message_loop/message_pump_glib.cc:263:25
    #34 0x5645d8d1a5e6 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:109
    #35 0x7f905cba4fc6 in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4afc6)

0x6140003c8d70 is located 304 bytes inside of 440-byte region [0x6140003c8c40,0x6140003c8df8)
freed by thread T0 (chrome) here:
    #0 0x5645cfb0e852 in operator delete(void*) /b/swarming/w/ir/kitchen-workdir/src/third_party/llvm/compiler-rt/lib/asan/asan_new_delete.cc:167:3
    #1 0x5645e198e733 in operator() buildtools/third_party/libc++/trunk/include/memory:2325:5
    #2 0x5645e198e733 in reset buildtools/third_party/libc++/trunk/include/memory:2638
    #3 0x5645e198e733 in operator= buildtools/third_party/libc++/trunk/include/memory:2504
    #4 0x5645e198e733 in payments::PaymentRequest::Init(mojo::InterfacePtr<payments::mojom::PaymentRequestClient>, std::__1::vector<mojo::StructPtr<payments::mojom::PaymentMethodData>, std::__1::allocator<mojo::StructPtr<payments::mojom::PaymentMethodData> > >, mojo::StructPtr<payments::mojom::PaymentDetails>, mojo::StructPtr<payments::mojom::PaymentOptions>) components/payments/content/payment_request.cc:129
    #5 0x5645d18f4de7 in payments::mojom::PaymentRequestStubDispatch::Accept(payments::mojom::PaymentRequest*, mojo::Message*) gen/third_party/blink/public/mojom/payments/payment_request.mojom.cc:1527:13
    #6 0x5645d902900e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:423:32
    #7 0x5645d903b1bd in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:869:42
    #8 0x5645d90398c7 in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:590:38
    #9 0x5645d9024b25 in mojo::Connector::ReadSingleMessage(unsigned int*) mojo/public/cpp/bindings/lib/connector.cc:476:51
    #10 0x5645d9026308 in mojo::Connector::ReadAllAvailableMessages() mojo/public/cpp/bindings/lib/connector.cc:505:10
    #11 0x5645d9077881 in Run base/callback.h:129:12
    #12 0x5645d9077881 in mojo::SimpleWatcher::OnHandleReady(int, unsigned int, mojo::HandleSignalsState const&) mojo/public/cpp/system/simple_watcher.cc:273
    #13 0x5645d8d13d51 in Run base/callback.h:99:12
    #14 0x5645d8d13d51 in base::debug::TaskAnnotator::RunTask(char const*, base::PendingTask*) base/debug/task_annotator.cc:99
    #15 0x5645d8d11525 in base::MessageLoopImpl::RunTask(base::PendingTask*) base/message_loop/message_loop_impl.cc:374:46
    #16 0x5645d8d127e9 in DeferOrRunPendingTask base/message_loop/message_loop_impl.cc:385:5
    #17 0x5645d8d127e9 in base::MessageLoopImpl::DoWork() base/message_loop/message_loop_impl.cc:473
    #18 0x5645d8d1a5e6 in HandleDispatch base/message_loop/message_pump_glib.cc:263:25
    #19 0x5645d8d1a5e6 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:109
    #20 0x7f905cba4fc6 in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4afc6)

previously allocated by thread T0 (chrome) here:
    #0 0x5645cfb0dc12 in operator new(unsigned long) /b/swarming/w/ir/kitchen-workdir/src/third_party/llvm/compiler-rt/lib/asan/asan_new_delete.cc:106:3
    #1 0x5645e198e3e1 in make_unique<payments::PaymentRequestSpec, mojo::StructPtr<payments::mojom::PaymentOptions>, mojo::StructPtr<payments::mojom::PaymentDetails>, std::__1::vector<mojo::StructPtr<payments::mojom::PaymentMethodData>, std::__1::allocator<mojo::StructPtr<payments::mojom::PaymentMethodData> > >, payments::PaymentRequest *, const std::__1::basic_string<char> &> buildtools/third_party/libc++/trunk/include/memory:3118:28
    #2 0x5645e198e3e1 in payments::PaymentRequest::Init(mojo::InterfacePtr<payments::mojom::PaymentRequestClient>, std::__1::vector<mojo::StructPtr<payments::mojom::PaymentMethodData>, std::__1::allocator<mojo::StructPtr<payments::mojom::PaymentMethodData> > >, mojo::StructPtr<payments::mojom::PaymentDetails>, mojo::StructPtr<payments::mojom::PaymentOptions>) components/payments/content/payment_request.cc:129
    #3 0x5645d18f4de7 in payments::mojom::PaymentRequestStubDispatch::Accept(payments::mojom::PaymentRequest*, mojo::Message*) gen/third_party/blink/public/mojom/payments/payment_request.mojom.cc:1527:13
    #4 0x5645d902900e in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:423:32
    #5 0x5645d903b1bd in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:869:42
    #6 0x5645d90398c7 in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:590:38
    #7 0x5645d9024b25 in mojo::Connector::ReadSingleMessage(unsigned int*) mojo/public/cpp/bindings/lib/connector.cc:476:51
    #8 0x5645d9026308 in mojo::Connector::ReadAllAvailableMessages() mojo/public/cpp/bindings/lib/connector.cc:505:10
    #9 0x5645d9077881 in Run base/callback.h:129:12
    #10 0x5645d9077881 in mojo::SimpleWatcher::OnHandleReady(int, unsigned int, mojo::HandleSignalsState const&) mojo/public/cpp/system/simple_watcher.cc:273
    #11 0x5645d8d13d51 in Run base/callback.h:99:12
    #12 0x5645d8d13d51 in base::debug::TaskAnnotator::RunTask(char const*, base::PendingTask*) base/debug/task_annotator.cc:99
    #13 0x5645d8d11525 in base::MessageLoopImpl::RunTask(base::PendingTask*) base/message_loop/message_loop_impl.cc:374:46
    #14 0x5645d8d127e9 in DeferOrRunPendingTask base/message_loop/message_loop_impl.cc:385:5
    #15 0x5645d8d127e9 in base::MessageLoopImpl::DoWork() base/message_loop/message_loop_impl.cc:473
    #16 0x5645d8d1a5e6 in HandleDispatch base/message_loop/message_pump_glib.cc:263:25
    #17 0x5645d8d1a5e6 in base::(anonymous namespace)::WorkSourceDispatch(_GSource*, int (*)(void*), void*) base/message_loop/message_pump_glib.cc:109
    #18 0x7f905cba4fc6 in g_main_context_dispatch (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4afc6)

SUMMARY: AddressSanitizer: heap-use-after-free buildtools/third_party/libc++/trunk/include/vector:1506:30 in begin
Shadow bytes around the buggy address:
  0x0c2880071150: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2880071160: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2880071170: fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa
  0x0c2880071180: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c2880071190: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c28800711a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd
  0x0c28800711b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c28800711c0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c28800711d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c28800711e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c28800711f0: fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==157886==ABORTING


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/46472.zip