Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Parsing nesting objects in JSON results in segment fault #3539

Closed
guhe120 opened this issue May 29, 2018 · 6 comments
Closed

Parsing nesting objects in JSON results in segment fault #3539

guhe120 opened this issue May 29, 2018 · 6 comments
Assignees
Labels
Milestone

Comments

@guhe120
Copy link

guhe120 commented May 29, 2018

Hi,

There is a stack overflow bug in json parser when parsing nesting objects.

There is a function named check_string_depth to handle such situation, it tries to make sure the nesting objects' depth is less than 100.

However the check is vulnerable and we can bypass it.

How to test:

1.Start nodeos

2.Execute:
python post.py

to send malicious json rpc request.

3.Observe the crash

@guhe120
Copy link
Author

guhe120 commented May 29, 2018

This is a submission to EOS bug bounty program and the bug credits to: Yuki Chen of Qihoo 360 Vulcan Team
json stack overflow.zip

@tbfleming tbfleming added the bug label May 29, 2018
@taokayan taokayan self-assigned this May 29, 2018
@taokayan
Copy link
Contributor

taokayan commented May 29, 2018

I've reproduced it, looks like it's a stack overflow bug:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8)
    frame #0: 0x00007fff68a0b22c libsystem_malloc.dylib`default_zone_malloc + 4
libsystem_malloc.dylib`default_zone_malloc:
->  0x7fff68a0b22c <+4>: pushq  %rbx
    0x7fff68a0b22d <+5>: pushq  %rax
    0x7fff68a0b22e <+6>: movq   %rsi, %rbx
    0x7fff68a0b231 <+9>: movq   0x39478e68(%rip), %rdi    ; lite_zone
Target 0: (nodeos) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8)
  * frame #0: 0x00007fff68a0b22c libsystem_malloc.dylib`default_zone_malloc + 4
    frame #1: 0x00007fff689fb201 libsystem_malloc.dylib`malloc_zone_malloc + 103
    frame #2: 0x00007fff689fa50b libsystem_malloc.dylib`malloc + 24
    frame #3: 0x00000001029161b8 libc++.1.dylib`operator new(unsigned long) + 40
    frame #4: 0x00000001006d1303 nodeos`fc::mutable_variant_object::mutable_variant_object() + 19
    frame #5: 0x00000001006f2754 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 52
    frame #6: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #7: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #8: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #9: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #10: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #11: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #12: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #13: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #14: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #15: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #16: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #17: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #18: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #19: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #20: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #21: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #22: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #23: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #24: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #25: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #26: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #27: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #28: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #29: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #30: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #31: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #32: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #33: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #34: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #35: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #36: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #37: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #38: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #39: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #40: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #41: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
    frame #42: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #43: 0x00000001006f2884 nodeos`fc::variant_object fc::objectFromStream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 356
...
... (repeat many many times)...
...
...
    frame #10804: 0x00000001006e7f66 nodeos`fc::variant fc::variant_from_stream<std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >, (fc::json::parse_type)0>(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 390
    frame #10805: 0x00000001006e6f35 nodeos`fc::json::from_string(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, fc::json::parse_type) + 309
    frame #10806: 0x00000001001989d0 nodeos`std::__1::__function::__func<eosio::chain_api_plugin::plugin_startup()::$_0, std::__1::allocator<eosio::chain_api_plugin::plugin_startup()::$_0>, void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::function<void (int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>)>::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&, std::__1::function<void (int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>&&) + 240
    frame #10807: 0x00000001002da204 nodeos`void eosio::http_plugin_impl::handle_http_request<websocketpp::transport::asio::basic_socket::endpoint>(websocketpp::server<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >::connection_ptr) + 1076
    frame #10808: 0x00000001002d9d77 nodeos`void eosio::http_plugin_impl::create_server_for_endpoint<websocketpp::transport::asio::basic_socket::endpoint>(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&, websocketpp::server<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >&)::'lambda'(std::__1::weak_ptr<void>)::operator()(std::__1::weak_ptr<void>) const + 71
    frame #10809: 0x00000001002d9ccc nodeos`std::__1::__function::__func<void eosio::http_plugin_impl::create_server_for_endpoint<websocketpp::transport::asio::basic_socket::endpoint>(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&, websocketpp::server<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >&)::'lambda'(std::__1::weak_ptr<void>), std::__1::allocator<void eosio::http_plugin_impl::create_server_for_endpoint<websocketpp::transport::asio::basic_socket::endpoint>(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&, websocketpp::server<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >&)::'lambda'(std::__1::weak_ptr<void>)>, void (std::__1::weak_ptr<void>)>::operator()(std::__1::weak_ptr<void>&&) + 44
    frame #10810: 0x00000001002fe399 nodeos`websocketpp::connection<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >::process_handshake_request() + 521
    frame #10811: 0x00000001002fcfd8 nodeos`websocketpp::connection<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint> >::handle_read_handshake(std::__1::error_code const&, unsigned long) + 1144
    frame #10812: 0x00000001002ed43c nodeos`websocketpp::transport::asio::connection<eosio::detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint>::transport_config>::handle_async_read(std::__1::function<void (std::__1::error_code const&, unsigned long)>, boost::system::error_code const&, unsigned long) + 652

@moskvanaft moskvanaft added this to the Version 1.0 milestone May 29, 2018
@moskvanaft moskvanaft self-assigned this May 29, 2018
@abitmore
Copy link

Just FYI this is an old bug which has been reported to EOS team one month before. Also commented here a5f1a20#commitcomment-29160834

Related fix in BitShares:

If there is a bounty for this, it should belong to BitShares Dev Team.

@newskysecurity
Copy link

check_string_depth implementation is naive, best approach is to check object tree depth while parsing.

@gleehokie
Copy link
Contributor

@abitmore thanks for bringing this to our attention, you are correct, we did receive a report of this issue from the BitShares Dev team about a month ago, but we failed to act then and lost track of it (we didn't create a GitHub issue 🤦‍♂️) so the vulnerability was still in our code when 360.cn brought it to our attention therefore they are eligible for the bounty.

I understand this may be frustrating for the BitShares Dev team. Please do continue to report vulnerabilities to us and you are welcome to do so via the https://hackerone.com/eosio bug bounty program.

@notchxor
Copy link

@gleehokie I think both deserves the bounty, Specially knowing that the bitshares-dev team report the problem without having any financial benefit. Also 360 received a huge backslash for reporting other problem, I think giving the bounty to both parties would be a responsable community oriented approach that would encourage more contributions.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

8 participants