{"id":944,"date":"2021-09-06T07:42:39","date_gmt":"2021-09-06T07:42:39","guid":{"rendered":"https:\/\/hackemall.live\/?p=944"},"modified":"2021-09-06T09:21:11","modified_gmt":"2021-09-06T09:21:11","slug":"write-up-counter-strike-squirrel-offensive-alles-ctf-2021","status":"publish","type":"post","link":"https:\/\/hackemall.live\/index.php\/2021\/09\/06\/write-up-counter-strike-squirrel-offensive-alles-ctf-2021\/","title":{"rendered":"Write-up \ud83d\udd25&#8221;Counter Strike: Squirrel Offensive&#8221; &#8211; ALLES! CTF 2021"},"content":{"rendered":"\n<p class=\"has-medium-font-size\">This challenge involves an old version of CS:GO VScript, which is vulnerable to&nbsp;<a href=\"https:\/\/github.com\/albertodemichelis\/squirrel\/issues\/220\">a UAF bug and a type confusion bug<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">UAF by resizing array in sort compare function<\/h2>\n\n\n\n<p class=\"has-medium-font-size\">The sort function of squirrel array is&nbsp;<code>array_sort<\/code>&nbsp;in&nbsp;<code>sqbaselib.cpp<\/code>, which will call&nbsp;<code>_qsort<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"69\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image.png\" alt=\"\" class=\"wp-image-946\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image.png 893w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-300x23.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-768x59.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-750x58.png 750w\" sizes=\"(max-width: 893px) 100vw, 893px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">The&nbsp;<code>r<\/code>&nbsp;index passed into&nbsp;<code>_qsort<\/code>&nbsp;is fixed at the beginning, so by abusing&nbsp;<code>array.resize<\/code>&nbsp;in compare function, we can retrieve dangling reference to freed objects through compare function parameters.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">By freeing a string then overlap it with an array, the&nbsp;<code>_len<\/code>&nbsp;field of the freed&nbsp;<code>SQString<\/code>&nbsp;object will be overwritten by the&nbsp;<code>_sharedstate<\/code>&nbsp;field of the newly created&nbsp;<code>SQArray<\/code>. It&#8217;s a pointer so the value will be very large, and we can use the dangling string to do arbitrary reading over a large heap space after it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Type confusion in regexp functions<\/h2>\n\n\n\n<p class=\"has-medium-font-size\"><code>_regexp_*<\/code>&nbsp;functions in&nbsp;<code>sqstdstring.cpp<\/code>&nbsp;retrieve&nbsp;<code>SQRex<\/code>&nbsp;object from the current object using <code>SETUP_REX<\/code> macro:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"86\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-1.png\" alt=\"\" class=\"wp-image-947\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-1.png 895w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-1-300x29.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-1-768x74.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-1-750x72.png 750w\" sizes=\"(max-width: 895px) 100vw, 895px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">The&nbsp;<code>typetag<\/code>&nbsp;parameter is&nbsp;<code>0<\/code>, means that it will not check for type mismatch. So we can call&nbsp;<code>_regexp_*<\/code>&nbsp;functions using any&nbsp;<code>instance<\/code>&nbsp;object (examples: self-defined classes, external library classes like CS:GO script classes).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Addresses leaking<\/h2>\n\n\n\n<p class=\"has-medium-font-size\">As we have a long string by using UAF bug above, we can just spray a lot of&nbsp;<code>CScriptKeyValues<\/code>&nbsp;and find one of them using last 2 bytes of&nbsp;<code>SQInstance::vtable<\/code>&nbsp;as they will not be affected by Windows ASLR, then use confusion to watch for changes to&nbsp;<code>_userpointer<\/code>&nbsp;field. But there are other&nbsp;<code>instance<\/code>&nbsp;objects too, and we have no way to be sure that it&#8217;s a&nbsp;<code>CScriptKeyValues<\/code>&nbsp;object.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">Fortunately, the&nbsp;<code>tostring<\/code>&nbsp;method will return the type name and the address in memory of any object. For number and string it will just return the value. But we overlapped the freed string with an array, so we can get address of it by calling&nbsp;<code>tostring<\/code>&nbsp;on the array. We can keep allocate new&nbsp;<code>CScriptKeyValues<\/code>&nbsp;object until we get one that lies after our long string and in the range that we can read its data. I won&#8217;t go into detail of Source Engine heap in this writeup, but most of the time we will get a satisfied object without triggering Squirrel timeout watchdog.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">By reading the&nbsp;<code>CScriptKeyValues<\/code>&nbsp;object, we can get these values:<\/p>\n\n\n\n<ul class=\"has-medium-font-size\"><li>Pointer to&nbsp;<code>SQInstance::vtable<\/code>, which can be used to calculate&nbsp;<code>vscript.dll<\/code>&nbsp;base address for ROP gadgets<\/li><li>Pointer to&nbsp;<code>_userpointer<\/code>&nbsp;of that object<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a href=\"https:\/\/gist.github.com\/chung96vn\/4fa186bfa0a94b7b53d8420a8f12e283#path-of-exploitation\"><\/a><\/h2>\n\n\n\n<h2 class=\"wp-block-heading\">Path of exploitation<\/h2>\n\n\n\n<p class=\"has-medium-font-size\">My approach is to use a CS:GO script class,&nbsp;<code>CScriptKeyValues<\/code>. Squirrel will panic if you attempt to modify the prototype after 1 instance of a class has been created. Since in map loading, there&#8217;re no instance of this class would be created, we can modify its prototype:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"69\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-2.png\" alt=\"\" class=\"wp-image-948\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-2.png 893w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-2-300x23.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-2-768x59.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-2-750x58.png 750w\" sizes=\"(max-width: 893px) 100vw, 893px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">When we call any method of a CS:GO script class,&nbsp;<code>CSquirrelVM::TranslateCall<\/code>&nbsp;in&nbsp;<code>vsquirrel.cpp<\/code>&nbsp;will be called. It will access&nbsp;<code>_userpointer<\/code>&nbsp;field of the object to get binding information:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"891\" height=\"403\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-3.png\" alt=\"\" class=\"wp-image-949\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-3.png 891w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-3-300x136.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-3-768x347.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-3-750x339.png 750w\" sizes=\"(max-width: 891px) 100vw, 891px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\"><code>_regexp_constructor<\/code>&nbsp;will create a new&nbsp;<code>SQRex<\/code>&nbsp;class and store it in&nbsp;<code>_userpointer<\/code>&nbsp;field. That means we can control&nbsp;<code>pContext<\/code>. Below is&nbsp;<code>InstanceContext_t<\/code>&nbsp;struct:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"892\" height=\"145\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-4.png\" alt=\"\" class=\"wp-image-950\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-4.png 892w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-4-300x49.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-4-768x125.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-4-750x122.png 750w\" sizes=\"(max-width: 892px) 100vw, 892px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">Below is&nbsp;<code>SQRex<\/code>&nbsp;struct:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"893\" height=\"315\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-5.png\" alt=\"\" class=\"wp-image-951\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-5.png 893w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-5-300x106.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-5-768x271.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-5-750x265.png 750w\" sizes=\"(max-width: 893px) 100vw, 893px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\"><code>pClassDesc<\/code>&nbsp;field overlaps with&nbsp;<code>_bol<\/code>&nbsp;field. When we call&nbsp;<code>_regexp_search(str)<\/code>,&nbsp;<code>_bol<\/code>&nbsp;field will be set to the beginning of&nbsp;<code>str<\/code>. So we can craft a fake&nbsp;<code>ScriptClassDesc_t<\/code>&nbsp;object using a string. Below is&nbsp;<code>ScriptClassDesc_t<\/code>&nbsp;struct:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"896\" height=\"297\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-6.png\" alt=\"\" class=\"wp-image-952\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-6.png 896w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-6-300x99.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-6-768x255.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-6-750x249.png 750w\" sizes=\"(max-width: 896px) 100vw, 896px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">Below is&nbsp;<code>IScriptInstanceHelper<\/code>&nbsp;interface:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"162\" src=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-7.png\" alt=\"\" class=\"wp-image-953\" srcset=\"https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-7.png 895w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-7-300x54.png 300w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-7-768x139.png 768w, https:\/\/hackemall.live\/wp-content\/uploads\/2021\/09\/image-7-750x136.png 750w\" sizes=\"(max-width: 895px) 100vw, 895px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-medium-font-size\">We can craft a fake&nbsp;<code>IScriptInstanceHelper<\/code>&nbsp;object to control the virtual method table.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">Fortunately, Squirrel string is not null-terminated, so we don&#8217;t have to worry about null bytes.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">In conclusion, the fake object will look like this:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\"><table><tbody><tr><td><strong>Offset<\/strong><\/td><td><strong>Content<\/strong><\/td><\/tr><tr><td>0x0<\/td><td>pivot gadget<\/td><\/tr><tr><td>&#8230;<\/td><td>padding<\/td><\/tr><tr><td>0x2c<\/td><td><code>_userpointer + 0x4<\/code>&nbsp;(<code>_bol<\/code>)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"has-medium-font-size\">Thanks ALLES! team for organizing a great CTF with awesome challenges, and allowed late submission of&nbsp;\ud83d\udd25&nbsp;challenges.<\/p>\n\n\n\n<p class=\"has-medium-font-size\">Source Engine is a mature engine with a lot of functions, and use a lot of unsafe memory code. With the f.mdact that any people can host dedicated servers, it&#8217;s a huge attack surface. It&#8217;s sad that Valve never bothers fixing security bugs in the engine quickly. I really hoped that they will pick up the pace after secret club&#8217;s callout, but seems like they will never do that.<\/p>\n\n\n\n<p>See POC <a rel=\"noreferrer noopener\" href=\"https:\/\/gist.github.com\/kungfulon\/c50323cf6ae54104e3c65b2b30804cc1#file-exp-nut\" data-type=\"URL\" data-id=\"https:\/\/gist.github.com\/kungfulon\/c50323cf6ae54104e3c65b2b30804cc1#file-exp-nut\" target=\"_blank\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Credit: Thank <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/bienpnn\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/bienpnn\" target=\"_blank\">@nyancat0131<\/a> for sharing!<\/strong><\/h2>\n","protected":false},"excerpt":{"rendered":"<p>This challenge involves an old version of CS:GO VScript, which is vulnerable to&nbsp;a UAF bug and a type confusion bug. UAF by resizing array in sort compare function The sort function of squirrel array is&nbsp;array_sort&nbsp;in&nbsp;sqbaselib.cpp, which will call&nbsp;_qsort: The&nbsp;r&nbsp;index passed into&nbsp;_qsort&nbsp;is fixed at the beginning, so by abusing&nbsp;array.resize&nbsp;in compare function, we can retrieve dangling reference [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":957,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,7,17,5],"tags":[],"_links":{"self":[{"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/posts\/944"}],"collection":[{"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/comments?post=944"}],"version-history":[{"count":11,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/posts\/944\/revisions"}],"predecessor-version":[{"id":968,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/posts\/944\/revisions\/968"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/media\/957"}],"wp:attachment":[{"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/media?parent=944"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/categories?post=944"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hackemall.live\/index.php\/wp-json\/wp\/v2\/tags?post=944"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}