{"id":675,"date":"2017-08-20T21:14:20","date_gmt":"2017-08-21T05:14:20","guid":{"rendered":"https:\/\/www.pnfsoftware.com\/blog\/?p=675"},"modified":"2017-08-21T12:57:20","modified_gmt":"2017-08-21T20:57:20","slug":"firmware-exploitation-with-jeb-part-1","status":"publish","type":"post","link":"https:\/\/www.pnfsoftware.com\/blog\/firmware-exploitation-with-jeb-part-1\/","title":{"rendered":"Firmware Exploitation with JEB: Part 1"},"content":{"rendered":"<p style=\"text-align: justify\"><span style=\"font-weight: 400\">In this series of blog posts I will show how JEB\u2019s <\/span><a href=\"https:\/\/www.pnfsoftware.com\/jeb\/mips\"><span style=\"font-weight: 400\">MIPS decompiler<\/span><\/a><span style=\"font-weight: 400\">\u00a0<sup class='footnote'><a href='#fn-675-1' id='fnref-675-1' onclick='return fdfootnote_show(675)'>1<\/a><\/sup> can help you find and exploit software vulnerabilities in embedded devices. To do so, we will use Praetorian\u2019s <\/span><a href=\"https:\/\/p16.praetorian.com\/blog\/getting-started-with-damn-vulnerable-router-firmware-dvrf-v0.1\"><span style=\"font-weight: 400\">Damn Vulnerable Router\u00a0<\/span><\/a><a href=\"https:\/\/p16.praetorian.com\/blog\/getting-started-with-damn-vulnerable-router-firmware-dvrf-v0.1\">Firmware <\/a><span style=\"font-weight: 400\"> (DVRF) written by <\/span><a href=\"https:\/\/twitter.com\/b1ack0wl\"><span style=\"font-weight: 400\">b1ack0wl<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">DVRF is a custom firmware made to run on a <\/span><span style=\"font-weight: 400\">Linksys E1550 router containing a bunch of memory corruption vulnerabilities. The goal of the DVRF is to serve as a playground to learn exploitation on the MIPS architecture. As far as I know, there are no write-ups of the challenges on the Internet.<\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">For the readers interested in testing the challenges by themselves, I suggest to follow the DVRF tutorial, and getting a <\/span><a href=\"https:\/\/blahcat.github.io\/2017\/07\/14\/building-a-debian-stretch-qemu-image-for-mipsel\/\"><span style=\"font-weight: 400\">complete MIPSEL Debian QEMU image<\/span><\/a><span style=\"font-weight: 400\"> as it allows the usual exploit development workflow on Linux, without any limits on the available tools.<\/span><\/p>\n<h1 style=\"text-align: justify\"><span style=\"font-weight: 400\">Recon<\/span><\/h1>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">First things first, I extracted the binaries from the firmware with <\/span><a href=\"https:\/\/github.com\/devttys0\/binwalk\"><span style=\"font-weight: 400\">binwalk<\/span><\/a><span style=\"font-weight: 400\">. Let\u2019s then do some recognition on the first challenge file:<\/span><\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n\r\nfile stack_bof_01\r\n\r\nstack_bof_01: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter \/lib\/ld-uClibc.so.0, not stripped\r\n\r\n<\/pre>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">After loading it in JEB we can see several interesting functions:<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/function_list.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-679\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/function_list-300x217.png\" alt=\"\" width=\"300\" height=\"217\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/function_list-300x217.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/function_list.png 560w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Among some classic libc interesting routines (<em>system<\/em>, <em>strcpy<\/em>\u2026), I noticed the aptly named \u201c<em>dat_shell<\/em>\u201d function.<\/span><\/p>\n<p style=\"text-align: justify\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/db52448690ccde49f36d3ffe65f2b3d0.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/db52448690ccde49f36d3ffe65f2b3d0.png\" alt=\"\" width=\"704\" height=\"84\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">As we see here, this function congratulates you for solving the challenge then spawns a shell with the call to <em>system<\/em>. We now know that we want to redirect the execution flow to the <em>dat_shell<\/em> function.<\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Next, we see that the binary calls \u201c<em>strcpy<\/em>\u201d and that may just be a textbook case of buffer overflow. So let\u2019s check the main function, where <em>strcpy<\/em> is called.<\/span><\/p>\n<p style=\"text-align: justify\"><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/06df0f6ccea626921ade97805d73b453.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/06df0f6ccea626921ade97805d73b453.png\" alt=\"\" width=\"597\" height=\"222\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">First, it checks if we provided a command-line argument, and welcomes us. Second, it copies user input in a local variable and prints what we entered. Finally, it tells us to \u201cTry Again\u201d and then returns. Fortunately, <em>strcpy<\/em>\u00a0does not check its input size, which results in a stack buffer overflow as the challenge\u2019s name indicates. <\/span><\/p>\n<h1 style=\"text-align: justify\"><span style=\"font-weight: 400\">Building the Exploit&#8230;<\/span><\/h1>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">As you would do in a similar situation on a x86 binary, let\u2019s first run the binary inside a debugger with a large parameter to verify the overflow. <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">To do this, I started a gdbserver on my QEMU VM and attached to it with JEB\u2019s debugger interface (see the <\/span><a href=\"https:\/\/www.pnfsoftware.com\/jeb\/manual\/debugging\/\"><span style=\"font-weight: 400\">debugging manual<\/span><\/a><span style=\"font-weight: 400\"> for more info). In MIPS ISA, the return address from a routine call is stored in a specific register called <em>$ra,<\/em> which is also filled from the stack as you normally see on x86. It then jumps to that saved <\/span><span style=\"font-weight: 400\">return address.<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/ra_from_stack.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-680\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/ra_from_stack.png\" alt=\"\" width=\"287\" height=\"97\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">In our binary, we confirm that the return address is user-controlled by providing a large parameter &#8212; a series of 0x4F bytes &#8211;, and displaying the registers state after the <em>strcpy<\/em> call:<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/registers_jeb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-681\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/registers_jeb-300x98.png\" alt=\"\" width=\"300\" height=\"98\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/registers_jeb-300x98.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/registers_jeb.png 476w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Let&#8217;s check the stackframe that I reconstructed to calculated the appropriate padding. You can access that view with the Ctrl+Alt+k shortcut in the function of your choice. I changed the type of the variable <em>buf<\/em> to a char array of all the available size between the start of the variable and the next one. This gave me 200 bytes.<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/stackframe.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-703\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/stackframe-300x77.png\" alt=\"\" width=\"300\" height=\"77\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/stackframe-300x77.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/stackframe.png 398w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>The variables var04 and var08 are in fact the saved return address and the saved frame pointer of the main function.<span style=\"font-weight: 400\"> The result is that this offset is at 204 bytes because we fill the buffer with 200 bytes and overwrite the save frame pointer with four more. Let\u2019s try the following exploit:<\/span><\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n\r\n#!\/usr\/bin\/python\r\n\r\npadding = &quot;O&quot;* 204\r\n\r\ndat_shell_addr = &quot;\\x50\\x09\\x40&quot; # Partial overwrite with little-endian arch\r\n\r\npayload = padding + dat_shell_addr\r\n\r\nwith open(&quot;input&quot;, &quot;wb&quot;) as f:\r\n\r\nf.write(payload)\r\n\r\n<\/pre>\n<h1 style=\"text-align: justify\"><span style=\"font-weight: 400\">\u2026Is Not So Easy<\/span><\/h1>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Surprisingly, our dummy exploit makes the program segfaults at the address <em>0x400970<\/em> &#8212; within the <em>dat_shell<\/em> routine. Let\u2019s take a look at this address in JEB native view:<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/dat_shell_prologue.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-682\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/dat_shell_prologue-300x106.png\" alt=\"\" width=\"300\" height=\"106\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/dat_shell_prologue-300x106.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/dat_shell_prologue.png 514w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">We can see here a memory access to the address computed by adding the offset <em>0x801C<\/em> to the global pointer register <em>$gp<\/em>. The problem here is that <em>$gp<\/em> was initially set at the beginning of the routine from the <em>$t9<\/em> register (see <em>0x4000958<\/em> above). <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">So, where does the value in <em>$t9<\/em> comes from? The answer lies in the way routines are usually called on MIPS (the calling convention): the <em>$t9<\/em> register is first set to the address of the target routine, and is then branched to, for example with a <em>jalr $t9<\/em> instruction (see MIPS ISA\u00a0<\/span><a href=\"http:\/\/math-atlas.sourceforge.net\/devel\/assembly\/mipsabi32.pdf\"><span style=\"font-weight: 400\">p.50<\/span><\/a><span style=\"font-weight: 400\">). The global pointer <em>$gp<\/em>\u00a0is then initialized with <em>$t9<\/em> and serves to compute various offsets, in particular to other functions that will be called, hence the fact that it absolutely needs to be correct.<\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">In other words, if the value of <em>$t9<\/em> is not the address of <em>dat_shell<\/em> when executing this routine, there is a good chance an invalid memory access will happen during the routine execution. To build a successful exploit, we need to load an arbitrary value from the stack into <em>$t9<\/em> and then branch to it, as it was a real function call. <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">To do so, we need a \u201cgadget\u201d, that is a series of instructions implementing the previously described behavior that we can jump to. In the search of this gadget, let\u2019s first check what dynamic libraries are loaded with the \u201clibs\u201d debugger command.<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/libs.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-683\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/libs-300x47.png\" alt=\"\" width=\"300\" height=\"47\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/libs-300x47.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/libs.png 494w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Luckily, we have three libraries loaded at fixed memory addresses: <em>libc.so.0<\/em>, <em>libgcc_s.so.0<\/em> and <em>ld-uClibc.so.0<\/em>. <\/span><\/p>\n<h1 style=\"text-align: justify\"><span style=\"font-weight: 400\">Interlude: ROP Gadget Finder Plugin for JEB<\/span><\/h1>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Using gadgets is a common need to build Return-Oriented-Programming (ROP) exploits, so I decided to develop a gadget finder plugin <sup class='footnote'><a href='#fn-675-2' id='fnref-675-2' onclick='return fdfootnote_show(675)'>2<\/a><\/sup>. Also, rather than searching gadgets from native instructions I decided to use <strong>JEB Intermediate Representation<\/strong> (IR), such that I could find gadgets on all architectures handled by JEB transparently.<\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">The end result is that when loading the three previously mentioned libraries in JEB, the plugin creates a view with all the gadgets:<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/mips_gadgets-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-701\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/mips_gadgets-1-300x56.png\" alt=\"\" width=\"300\" height=\"56\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/mips_gadgets-1-300x56.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/mips_gadgets-1-768x145.png 768w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/mips_gadgets-1.png 797w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">The output is free of duplicated gadgets and alphabetically ordered to ease the process of finding interesting gadgets. <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">So, how does it work exactly? Using JEB\u2019s API, the plugin converts the native code to the IR used in the first stage of our decompilation pipeline. At this stage, all the side-effects of the native instructions are exposed and no optimizations have been made yet. <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">To find gadgets &#8212; a series of instructions ending with a branch &#8211;, we simply search for the assignments on the program counter register and iterate backwards until another assignment on that register is made. The last step is to filter out relative jumps &#8212; which can not really be controlled during an exploit &#8212; and we got ourselves a good list of ROP gadgets. <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Again, this method works on all architectures as it is using only the IR.\u00a0<\/span><span style=\"font-weight: 400\">As an example, here is the same code running on an ARMv7 binary:<\/span><\/p>\n<h1><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/arm_gadgets.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-685\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/arm_gadgets-300x114.png\" alt=\"\" width=\"300\" height=\"114\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/arm_gadgets-300x114.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/arm_gadgets.png 599w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/h1>\n<p>The published code can be found <a href=\"https:\/\/github.com\/pnfsoftware\/PleaseROP\">here<\/a>:<\/p>\n<h1 style=\"text-align: justify\"><span style=\"font-weight: 400\">Interlude End<\/span><\/h1>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">Back to our challenge, using our plugin on the libc library, I found the following gadget at offset <em>0x6b20<\/em>:<\/span><\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/t9_gadget.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-686 size-medium\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/t9_gadget-300x50.png\" alt=\"\" width=\"300\" height=\"50\" \/><\/a><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">It copies a value from the top of the stack into the <em>$t9<\/em> register, and branches to the <em>$t9<\/em> register\u2026 perfect! <\/span><\/p>\n<p style=\"text-align: justify\"><span style=\"font-weight: 400\">The plan is therefore to use the vulnerable <em>strcpy<\/em> to execute this gadget first, such that <em>dat_shell<\/em> address will be called as a normal routine call would do. After deactivating Address Space Layout Randomization (ASLR) on our test machine, we can use the previously found libc base address for the exploit. The final exploit looks like this:<\/span><\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#!\/usr\/bin\/python\r\n\r\nimport struct\r\n\r\n# LW $t9, 0($sp); JALR $t9;\r\n\r\ngadget_offset = 0x6b20\r\n\r\nlibc_base = 0x77eea000\r\n\r\ngadget_addr = struct.pack(&quot;&lt;I&quot;, libc_base + gadget_offset)\r\n\r\npayload = &quot;&quot;\r\n\r\npayload += &quot;A&quot;*204 # padding\r\n\r\npayload += gadget_addr\r\n\r\npayload += &quot;\\x50\\x09\\x40&quot;\r\n\r\nwith open(&quot;input&quot;, &quot;wb&quot;) as f:\r\n    f.write(payload)\r\n<\/pre>\n<p>And here we go!<\/p>\n<p><a href=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/congrats.png\"><br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-698\" src=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/congrats-300x74.png\" alt=\"\" width=\"409\" height=\"101\" srcset=\"https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/congrats-300x74.png 300w, https:\/\/www.pnfsoftware.com\/blog\/wp-content\/uploads\/2017\/08\/congrats.png 618w\" sizes=\"auto, (max-width: 409px) 100vw, 409px\" \/><\/a><\/p>\n<p>Special thanks to <a href=\"https:\/\/twitter.com\/b1ack0wl\">@b1ack0wl<\/a>\u00a0for the challenges and help and to <a href=\"https:\/\/twitter.com\/yrp604\">@yrp604<\/a>\u00a0for the review. This post was also co-authored with our own <a href=\"https:\/\/twitter.com\/joancalvet\">@joancalvet<\/a>.<\/p>\n<h3 style=\"text-align: justify\"><span style=\"font-weight: 400\">Stay tuned for more MIPS exploitation blog posts!<\/span><\/h3>\n<div class='footnotes' id='footnotes-675'>\n<div class='footnotedivider'><\/div>\n<ol>\n<li id='fn-675-1'> In this blog, we use JEB 2.3.3, which will roll out the week of Aug 21-25. <span class='footnotereverse'><a href='#fnref-675-1'>&#8617;<\/a><\/span><\/li>\n<li id='fn-675-2'> The gadget finder plugin will be published on GitHub later this week. <span class='footnotereverse'><a href='#fnref-675-2'>&#8617;<\/a><\/span><\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this series of blog posts I will show how JEB\u2019s MIPS decompiler\u00a01 can help you find and exploit software vulnerabilities in embedded devices. To do so, we will use Praetorian\u2019s Damn Vulnerable Router\u00a0Firmware (DVRF) written by b1ack0wl. DVRF is a custom firmware made to run on a Linksys E1550 router containing a bunch of &hellip; <a href=\"https:\/\/www.pnfsoftware.com\/blog\/firmware-exploitation-with-jeb-part-1\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Firmware Exploitation with JEB: Part 1<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,13],"tags":[],"class_list":["post-675","post","type-post","status-publish","format-standard","hentry","category-jeb2","category-native-code"],"_links":{"self":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/675","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/comments?post=675"}],"version-history":[{"count":0,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/675\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/media?parent=675"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/categories?post=675"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pnfsoftware.com\/blog\/wp-json\/wp\/v2\/tags?post=675"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}